JVM

37 阅读3分钟

内存模型

运行时数据区

PC计数器

当前线程所执行的行号指示器,通过计算器确定下一条命令

JVM栈

每个线程都有一个私有的虚拟机栈,可能会抛出StackOverlfowError、OutMemoryError

每个方法执行都会创建一个栈帧

栈帧:

局部变量表

操作数栈

动态链接

方法返回地址

存储对象实例,数组、字符串常量池

方法区 (所有线程共享,非线程安全,只有一个,存储:类信息、常量、静态变量)抛出OutMemoryError

运行时常量池

本地方法栈

类加载机制(类加载器的双亲委派加载机制)

image.png

类加载原则:双亲委派原则

一个类加载器自己不会加载,优先找父类加载

类加载过程

加载 -> 链接 -> 初始化 -> 使用 -> 卸载

1、加载:通过类名查找二进制字节码文件,创建Class对象。

通过一个类的全限定名来获取定义此类的二进制字节流

将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。

在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口。

2、链接:

验证:验证类的正确性和安全性,可以采用-Xverify:none参数来关闭大部分的类验证措施。

准备:为类的静态变量分配内存,并将其赋默认值。

只对static修饰的静态变量进行内存分配、赋默认值(如0、0L、null、false等)。

对final的静态字面值常量直接赋初值(赋初值不是赋默认值,如果不是字面值静态常量,那么会和静态变量一样赋默认值)

解析:将常量池中的符号引用替换为直接引用(内存地址)的过程

3、初始化:为类的静态变量赋初值。

赋初值两种方式:

定义静态变量时指定初始值。如 private static String x="123";

在静态代码块里为静态变量赋值。如 static{ x="123"; }

注意:只有对类的主动使用才会导致类的初始化。

4、clinit 与 init

clinit指的是类构造器,主要作用是在类加载过程中的初始化阶段进行执行,执行内容包括静态变量初始化和静态块的执行。

注意事项:

    1. 如果类中没有静态变量或静态代码块,那么clinit方法将不会被生成。
    2. 在执行clinit方法时,必须先执行父类的clinit方法。
    3. clinit方法只执行一次。
    4. static变量的赋值操作和静态代码块的合并顺序由源文件中出现的顺序决定。

init指的是实例构造器,主要作用是在类实例化过程中执行,执行内容包括成员变量初始化和代码块的执行。

注意事项:

    1. 如果类中没有成员变量和代码块,那么clinit方法将不会被生成。
    2. 在执行init方法时,必须先执行父类的init方法。
    3. init方法每实例化一次就会执行一次。
    4. init方法先为实例变量分配内存空间,再执行赋默认值,然后根据源码中的顺序执行赋初值或代码块。

5、卸载阶段:程序退出

G1垃圾回收