JVM 内存模型
1.类装载子系统把 .class 文件装载运行时数据区(内存模型)
2.执行引擎去数据区执行字节码文件
JVM 内存区域
1. 堆(共享)
堆就是被所有线程共享的一块内存区域.这块内存区域的唯一目的就是存放内存实例.
几乎所有对象实例都在这里分配内存.
但不是所有对象都保存在堆中,如果 java 在必要的时候,可以把对象分配到栈中,从而自动销毁对象,那必然能减少一些垃圾回收的开销,同时提高执行效率。
逃逸分析:它并不是直接优化代码的手段,而是为其他优化手段提高依据的分析技术,逃逸分析的主要作用就是分析对象的作用域。当一个对象在方法中被定义后,他可能被外部方法引用,例如作为调用参数传到其他方法,这种行为叫做方法逃逸。甚至该对象可能被外部线程访问到,例如赋值被类变量或可以在其他线程中访问的实例变量,称为线程逃逸,通过逃逸分析,把不逃逸的对象找出,并在栈中分配内存,那他就可以随着方法桢出栈而销毁。这样大量的对象就不需要存放在堆中,垃圾收集系统的压力也会变小很多。
线程共享的 Java 堆中可能还会为每个线程划分线程私有的分配缓冲区(Thread Local Allocation Buffer,TLAB),但存放的依旧是对象实例.对象被 new 出来的时候会同时生成一个对象头信息,其中有一个类指针,指向方法区中的类元信息
对象创建的五种方式:
- new 关键字(调用了构造方法)
User user=new User();
- 利用反射机制(调用了构造方法)
User user=User.class.newInstance();
- 利用 Constructor 类的 newInstance()(调用了构造方法)
Constructor<User> constructor=User.class.getConstructor();
User user=constructor.newInstance();
- 使用 clone 方法(没有调用构造方法)
User user2=user1.clone();
- 反序列化(没有调用构造方法)
ObjectInputStream in=new ObjectInputStream(new FileInputStream("user.obj"));
User user=(User) in.readObject();
对象创建的过程大致如下:
- 虚拟机遇到 new 指令,到常量池定位到到这个类的符号引用,
- 检查符号引用代表的类是否被加载、解析、初始化过,如果没有,那必须先执行相应
的类加载过程 - 虚拟机为对象分配内存
- 虚拟机将分配到的内存空间都初始化为 0 值
- 虚拟机对对象进行必要的设置
- 执行方法,成员变量进行初始化
新建出来的对象,存放在 Eden,当 Eden 存满,会触发 JVM 的 minor gc 命令,会把 Eden 中没有引用的对象清理掉, 这些对象被转移到 Survivor 取,并且每经历一次 minor gc 对象的分代年龄就会+1,当分代年龄达到 15,就会存放到老年代中,当老年代中的对象存满时,会执行 full gc,如果还是不能清理出内存空间,就会产生内存溢出
2. 栈(线程栈/虚拟机栈)(私有)
虚拟机栈符合栈数据结构的特征,先进后出(主方法进站子方法再进栈,先执行子方法,子方法执行完出栈,最后主方法再出栈)。
栈是线程私有的,每个线程有对应的独立栈区,它的生命周期与线程相同。栈是描述 Java 方法执行的内存模型,每个方法在执行的同时都会创建这个方法对应的栈帧,用于存储方法内部局部变量表、操作数栈、动态链接、方法出口等信息,每一个方法从调用到执行完的过程就对应着一个栈桢在栈中入栈到出栈的过程。
栈帧内存模型:
- 局部变量表:存放了编译期可知的各种基本数据类型、以及对象变量和其对象的地址
(指向堆中), 内存空间在编译期间完成分配,当进入一个方法时,这个方法需要在帧中分配
多大的局部空间是完全确定的. - 操作数栈:存放操作数,遇到运算符取数据
- 动态链接:存放线程中每个方法的指令码的内存地址
- 方法出口:方法执行完要返回到父方法(线程)中的哪一条指令
3. 本地方法栈
本地方法栈也是线程私有的,用于存放系统本地的方法,一般用于 java 访问系统中的 c 语言写的方法.
4. 方法区(元空间)(共享)
方法区存放在系统内存中,不在 jvm 虚拟机的内存中.
方法区存放常量,静态变量以及类元信息(类的方法,类的属性)
运行时常量池是方法区的一部分,《Java 虚拟机规范》对于方法区的管理是最宽松的,这个区域可以不用实现垃圾回收,对于内存策略,方法区的大小可以是固定的也可以是可扩展的,不同的虚拟机对于方法区有不同的实现,HotSpot 虚拟机在 Java 8 之前,方法区仅仅是逻辑上的空间,物理上位于堆中的永久代中。Java 8 中 HotSpot 虚拟机移除了堆中的永久代,使
用本地内存存储类元信息,称为元空间
5. 程序计数器(私有)
程序计数器是线程私有的。存放线程正在执行或者将要执行的 JVM 指令存放的对应地址。
6.直接内存
直接内存并不是虚拟机运行时数据区的一部分,也不是虚拟机规范中定义的内存区域,但也被频繁使用.
JVM 内存间交互操作
Java 内存模型中定义了 8 个操作来完成主内存和工作内存的交互操作.
- read:把一个变量的值从主内存传输到工作内存中
- load:在 read 之后执行,把 read 得到的值放入工作内存的变量副本中
- use:把工作内存中一个变量的值传递给执行引擎
- assign:把一个从执行引擎接收到的值赋给工作内存的变量
- store:把工作内存的一个变量的值传送到主内存中
- write:在 store 之后执行,把 store 得到的值放入主内存的变量中
- lock:作用于主内存的变量
- unlock