9. java内存模型,垃圾回收机制,不可达算法
Java内存模型
Java内存模型结构分为:
- 线程共享区:堆、方法区
- 线程私有区:虚拟机栈、本地方法栈、程序计数器
JVM主要管理两种类型内存:堆和非堆。
堆是运行时数据区域,所有类实例和数组的内存均从此处分配。
非堆是JVM留给自己用的,包含方法区、JVM内部处理或优化所需的内存(如JIT Compiler,Just-in-time Compiler, 即时编译后的代码缓存)、每个类结构(如运行时常量池、字段和方法数据)以及方法和构造方法的代码。
堆内存模型
堆是用来存放对象实例,因此堆也是垃圾收集器管理的主要区域,故也称为GC堆。由于现在的垃圾收集器基本都采用分代收集算法,所以堆内存分为两大部分:新生代和老年代。比例为1:2。老年代主要存放应用程序中生命周期长的存活对象。新生代又分为三个部分:一个Eden区和两个Survivor区,比例为8:1:1。Eden存放新生的对象,Survivor存放每次垃圾回收后存放的对象。刚创建的对象都放入Eden,进过GC幸存下来的对象,就会被分配到survivorSpace0,依次类推直到被分配到老年代为止。在survivorSpace0和survivorSpace1都至少经过一次GC并幸存,如果幸存对象经过一定时间仍存在,则进入老年代。
方法区
方法区用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据
- 方法区经常和永久区关联在一起,但永久代和方法区不是一个概念,只是有的虚拟机用永久代来实现方法区,这样就可以用永久代GC来管理方法区,省去专门内存管理的工作
- 根据Java虚拟机规范的规定,当方法区无法满足内存分配的需求时,将抛出OOM异常
运行时常量池
运行时常量池是方法区的一部分,用于存放编译器生成的各种字面量和符号引用
方法区回收
- 常量池的回收。系统中任何对象没有引用常量池中的字面量、符号引用,当内存回收时,就会被清除。
- 无用类的回收。满足下述三个条件的无用类进行回收
- 该类所有的实例都已经被回收,也就是堆中不存在该类的任何实例
- 加载该类的ClassLoader已经被回收
- 该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。
虚拟机栈
虚拟机栈描述的是Java方法执行的内存模型,是线程私有的
- 每个方法在执行的时候都会创建一个栈帧,用于存储局部变量表、操作数栈、动态链接、方法出口等信息
- 每个方法从调用直至完成的过程,对应一个栈帧在虚拟机栈中入栈到出栈的过程
- 局部变量表主要存放一些基本类型的变量和对象句柄,他们可以是方法参数,也可以是方法的局部变量
本地方法栈
本地方法栈与Java虚拟机栈非常相似,也是线程私有的,主要异同点如下
- 虚拟机栈为虚拟机执行Java方法服务
- 本地方法栈为虚拟机执行Native方法服务
- 与虚拟机栈一样,本地方法栈区域也会抛出StackOverflowError和OOM异常
程序计数器
在多线程情况下,当线程数超过CPU数量或CPU内核数量时,线程之间就要根据时间片轮询抢夺CPU时间资源。也就是说,在任何一个时刻,一个处理器都只会执行一条线程中的指令。因此为了线程切换后能够恢复到正确的执行位置,每个线程都需要一个独立的程序计数器来记录其正在执行的字节码指令地址
程序计数器是线程私有的一块较小的内存空间,可以看做是当前线程所执行的字节码的行号指示器
- 如果线程正在执行的是一个Java方法,计数器记录的是正在执行的字节码指令的地址
- 如果正在执行的是Native方法,则计数器的值为空
- 程序计数器是唯一一个没有规定任何OOM的区域
后续主要介绍G1垃圾回收算法,待更新