简介JVM

简介: 简介JVM

一、内存分区

Java将内存分为了程序计数器、栈区、方法区、堆区。

1、程序计数器

程序计数器是内存中最小的区域,保存了下一条要执行指令的地址。

程序运行时,JVM就会把字节码加载起来,放到内存中,程序把指令从内存中取出来,放到CPU上执行这也就需要随时记住当前指令执行的位置,因为CPU是并发执行程序的,并且操作系统以线程为单位进行调度执行的,所以每个线程都有程序计数器。

2、栈

栈区主要保存的是局部变量和方法的调用信息,方法调用的时候就会涉及“入栈”操作,方法执行完之后就会涉及“出栈”的操作,局部变量也是类似。

栈空间也是比较小的,代码处理不当就会出现栈溢出异常。

每个进程都会分配一个栈区。

3、堆

堆区是内存空间中最大的区域,每次new出来的对象存在于堆区,对象的成员变量也就自然存在于堆区了。

一个进程会分配一个堆区,多个线程共享一个堆区。

4、方法区

方法区存放的是类对象,每次当.class文件编译形成.class文件,.class文件会被加载到内存中,也就会被JVM构造成类对象(加载的过程就是类加载),此处的类对象就会存放到方法区,类对象中的静态成员也会存放到方法区。

二、类加载

类加载是将.class文件加载到内存中,构建成类对象,类加载主要有Loading、Linking和Initiallizatio三步。

1、Loading

Loading过程是先找到对应的.class文件,然后打开并读取.class文件,会将读取并解析到的信息初步填写到类对象中,同时会初步生成一个类对象。

2、Linking

Linking来建立多个实体之间的联系,Linking又可以分为Verification、Preparation和Resolution三个阶段。

Verification

校验阶段,主要来验证读到的内容与规范中规定的格式是否完全匹配,如果返现格式不匹配,就会类加载失败,并且抛出异常。

Preparation

准备阶段,是正式为类中的变量分配内存并设置初始值,也就是给静态变量分配内存,并设置初值。

Resolution

解析阶段是Java虚拟机将常量池中的符号引用替换为直接引用的过程,也就对常量初始化的过程。

3、Initializing

是真正对类对象进行初始化,尤其是对静态成员

4、双亲委派模型

双亲委派模型其实是Loading的一个环节,描述的是JVM中的类加载器如何根据类的全限定名来找到.class文件的过程,也就是描述了找目录的过程。

JVM中提供了许多类加载器,每个类加载器负责一个片区,默认的类加载器主要有三个:

  • BootStrapClassLoader:负责加载标准库中的类。
  • ExtensionClassLoader:负责加载JDK扩展的类。
  • ApplicationClassLoader:负责加载当前项目目录中的类。

查找标准库中的类的过程

  1. 启动程序,先进入ApplicationClassLoader类加载器。
  2. ApplicationClassLoader就会检查它的父类是否已经加载过了,如果没有就调用父加载器ExtensionClassLoader。
  3. ExtensionClassLoader也会检查它的父类是否已经加载过了,如果没有就调用父加载器BootStrapClassLoader。
  4. BootStrapClassLoader会扫描自己负责的目录,一定会找到,找到后负责类加载的后序过程,直至查找环节结束。

查找自定义类的过程:


  1. 启动程序,先进入ApplicationClassLoader类加载器。
  2. ApplicationClassLoader就会检查它的父类是否已经加载过了,如果没有就调用父加载器ExtensionClassLoader。
  3. ExtensionClassLoader也会检查它的父类是否已经加载过了,如果没有就调用父加载器BootStrapClassLoader。
  4. BootStrapClassLoader会扫描自己负责的目录,如果没找到,就回到子加载器ExtensionClassLoader继续进行扫描。
  5. ExtensionClassLoader也会对自己负责的目录进行扫描,如果没找到,就回到子加载器ApplicationClassLoader继续进行扫描。
  6. ApplicationClassLoader对自己负责的目录进行扫描,如果找到就进行后续加载,否则就会抛出ClassNotFoundException异常。

三、垃圾回收

如果一直申请空间而不进行空间回收的话就会导致内存用完,也就是内存泄露的问题,对于栈和程序计数器不考虑垃圾回收,当方法或进程结束时所占用的空间也会随着释放,垃圾回收主要针对堆和方法区。

1、如何判断为垃圾?

引入引用计数

就是给对象再开辟一块区域用于引入计数器,当调用这个对象时,计数器就会加1,当引用失效的时候,计数器就会-1,如果计数器为0,该对象就会变为“垃圾”,进行回收。

但是也存在缺点:

空间利用率低,当对象本身空间很小时再增加一个计数器空间利用率低。

循环引用

public class T {
    public T t = null;
 
    public static void main(String[] args) {
        T t1 = new T();
        T t2 = new T();
        t1.t = t2;
        t2.t = t1;
        t1 = null;
        t2 = null;
    }
}

两个对象相互引用之后引用的t对象的程序计数器就为1,无法进行进行回收。

可达性分析

定义一个起始位置GCRoots,类似于深度优先遍历,对可以遍历到的对象进行标记,没标记的对象就是不可达,就成了垃圾。

例如:

GCRoots可以访问所有结点就不存在垃圾。

GCRoots无法访问C和F,C和F就是垃圾。

在Java语言中,可作为GC Roots的对象包含下面几种:

1. 虚拟机栈(栈帧中的本地变量表)中引用的对象;

2. 方法区中类静态属性引用的对象;

3. 方法区中常量引用的对象;

4. 本地方法栈中 JNI(Native方法)引用的对象。

2、如何进行垃圾回收?

标记清除

进行可达性标记,对于未标记的直接进行清除释放内存。

缺点:产生许多内存碎片。

复制算法

为了避免产生内存碎片,就引入了复制算法,将申请的内存一分为2,将不是垃圾的拷贝到另一边,将原来使用的一半空间整体释放。

缺点:

  • 空间利用率低。
  • 当垃圾较少时,复制开销较大。

标记整理

将不是垃圾的元素都拷贝到申请的空间的前面,后面的空间可以正常利用。类似于顺序表删除元素。

分代回收

对象新创建出来的时候先放在伊甸园,当伊甸园中的对象熬过一轮GC扫描,利用复制算法就会被拷贝到幸存区,在后序的几轮GC中幸存区的对象还是利用复制算法在幸存区之间来回拷贝,每一轮又会进行淘汰,在持续若干次之后,对象就会进入老年代,对象越老,继续存活的可能性就越大,老年的扫描频率低,并且老年代使用标记整理的方式进行回收。


分代回收中占用内存较大的对象可直接进入老年代。

目录
相关文章
|
存储 Java
【JVM】jvm简介特点和jvm在整个计算机框架中的位置
【JVM】jvm简介特点和jvm在整个计算机框架中的位置
54 0
|
5月前
|
监控 算法 Java
JVM调优-简介(一)
JVM调优-简介(一)
40 0
|
5月前
|
存储 Java 编译器
【JavaEE初阶】 JVM 运行时数据区简介
【JavaEE初阶】 JVM 运行时数据区简介
|
5月前
|
Oracle Java 关系型数据库
【JavaEE初阶】 JVM简介
【JavaEE初阶】 JVM简介
|
自然语言处理 Java 编译器
Java及JVM简介
Java及JVM简介
|
存储 监控 Java
Java JVM监控工具JConsole简介
Java JVM监控工具JConsole简介
213 0
|
存储 算法 Java
Java虚拟机-垃圾回收简介
Java虚拟机-垃圾回收简介
111 0
Java虚拟机-垃圾回收简介
|
存储 XML Java
JVM虚拟机-Class文件简介
JVM虚拟机-Class文件简介
139 0
JVM虚拟机-Class文件简介
|
开发框架 安全 Oracle
Java基础一(Java 简介+JDK JRE JVM +如何搭建Java开发环境/Java 开发环境配置+Java 源程序与编译型运行区别)
Java 简介+java特性+JDK JRE JVM +如何搭建Java开发环境/Java 开发环境配置+Java 源程序与编译型运行区别)
248 1
Java基础一(Java 简介+JDK JRE JVM +如何搭建Java开发环境/Java 开发环境配置+Java 源程序与编译型运行区别)
|
1月前
|
存储 安全 Java
jvm 锁的 膨胀过程?锁内存怎么变化的
【10月更文挑战第3天】在Java虚拟机(JVM)中,`synchronized`关键字用于实现同步,确保多个线程在访问共享资源时的一致性和线程安全。JVM对`synchronized`进行了优化,以适应不同的竞争场景,这种优化主要体现在锁的膨胀过程,即从偏向锁到轻量级锁,再到重量级锁的转变。下面我们将详细介绍这一过程以及锁在内存中的变化。
37 4