JVM内存区域
运行时数据区域
线程私有的:
- 程序计数器
- 虚拟机栈
- 本地方法栈
线程共享的:
- 堆
- 方法区
- 直接内存 (非运行时数据区的一部分)
程序计数器
线程私有的,每一个线程都有一个自己的计数器,可以看作是当前线程所执行的字节码的行号指示器。
记录命令执行到第几行,记录代码执行位置。
通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等功能都需要依赖这个计数器来完成。
Java虚拟机栈
线程私有的,每一个线程都有一个自己的虚拟机栈,每一次方法调用,就会创建一个对应的栈帧,栈帧入栈执行代码,执行完毕出栈。
生命周期和线程相同,随着线程的创建而创建,随着线程的死亡而死亡。
本地方法栈
线程私有的,每一个线程都有一个自己的本地方法栈,与Java虚拟机栈类似。
Java虚拟机栈加载的是普通方法,本地方法加载的是native修饰的方法
native:在java中用native修饰的,表示这个方法不是java原生的
堆
线程共享,存放new的对象实例,几乎所有的对象实例及数组在这分配内存;字符串常量
JDK1.8后堆内存:
- 新生代:新创建的对象
- 老年代:经过新生代多次垃圾回收存活下来的对象存在老年代中
- 元空间:1.8后取代原来的永久代,即保存原来永久代的对象
元空间
存储.class信息,类的信息,方法的定义,静态变量等
GC垃圾回收
JVM垃圾回收分为两步,如何发现垃圾,如何回收垃圾。
线程私有不存在垃圾回收,只有线程共享的才会存在垃圾回收。
如何发现垃圾
引用计数法
给对象添加一个引用计数器
- 每被引用一次,计数器加1
- 引用失效,计数器减1
- 为0时当作垃圾回收
优点:简单,效率高,快
缺点:无法解决对象相互引用的问题
可达性分析算法(根搜索)
以一个节点GC Roots为起点,从这些节点开始向下搜索,节点所走过的路径称为引用链,当一个对象到GC Roots没有任何引用链相连的话,则证明此对象是不可用的,需要被回收
可作为GC Roots的有:
- 虚拟机栈中引用的对象
- 本地方法栈中引用的对象
- 方法区中静态属性引用的对象
- 方法区中常量引用的对象
如何回收垃圾
标记-清除算法
分为“标记”和“清除”两个阶段,首先标记访问过的对象,标记完成后统一回收所有没有标记的对象
缺点: 效率不高,标记清除后产生大量不连续的内存碎片
复制算法
为了解决效率问题,把内存划两等份,每次用其中一块,满了后把存活的对象copy到另一边去
缺点: 内存只有原来一半
标记-整理算法
让所有存活的对象都向一端移动,然后直接清理掉边界以外的内存,不会产生大量不连续碎片
分代收集算法(主流)
根据对象存活周期的不同采用不同算法
新生代:回收频率高,只有少量对象存活,采用标记-复制
老年代:对象存活率高,采用标记-整理