java之jvm

一、内存布局

java之jvm_第1张图片
(1)虚拟机栈。它保存局部变量和部分结果,并在方法调用和返回中起作用。
(2)本地方法栈。与虚拟机栈基本类似,区别在于虚拟机栈为虚拟机执行的 Java 方法服务,而本地方法栈则是为 Native 方法服务。
(3)程序计数器。程序计数器是一块较小的内存空间,可以看作当前线程所执行字节码的行号指示器。字节码解释器工作时通过改变计数器的值选取下一条执行指令。分支、循环、跳转、线程恢复等功能都需要依赖计数器完成。是唯一在虚拟机规范中没有规定内存溢出情况的区域。如果线程正在执行 Java 方法,计数器记录正在执行的虚拟机字节码指令地址。如果是本地方法,计数器值为 Undefined。
(4)方法区。方法区用于存储被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存等数据。
(5)堆。堆是虚拟机所管理的内存中最大的一块,被所有线程共享的,在虚拟机启动时创建。堆用来存放对象实例,Java 里几乎所有对象实例都在堆分配内存。

二、哪些对象是垃圾

1. 引用计数法

在对象中添加一个引用计数器,如果被引用计数器加 1,引用失效时计数器减 1,如果计数器为 0 则被标记为垃圾。原理简单,效率高,但是在 Java 中很少使用,因为存在对象间循环引用的问题,导致计数器无法清零。

2. 可达性分析法

(1)主流语言的内存管理都使用可达性分析判断对象是否存活。基本思路是通过一系列称为 GC Roots 的根对象作为起始节点集,从这些节点开始,根据引用关系向下搜索,搜索过程走过的路径称为引用链,如果某个对象到 GC Roots 没有任何引用链相连,则会被标记为垃圾。
(2)可作为GC ROOTS的对象

  • 1)Java 虚拟机栈(局部变量表)中的引用的对象。
  • 2)方法区中静态引用指向的对象。
  • 3)仍处于存活状态中的线程对象。
  • 4)Native 方法中 JNI 引用的对象。

垃圾回收算法

标记-清除算法

(1)效率一般,缺点是会造成内存碎片问题。

复制算法

(2)复制算法是所有算法里面效率最高的,缺点是会造成一定的空间浪费。

标记-压缩算法

效率比前两者要差,但没有空间浪费,也消除了内存碎片问题。

分代回收策略

研究表明,大部分对象的生命周期都很短,其他对象则很可能会存活很长时间。大部分死的快,其他的活的长。这个假设我们称之为弱代假设。
现在的垃圾回收器,都会在物理上或者逻辑上,把这两类对象进行区分。我们把死的快的对象所占的区域,叫作年轻代。把其他活的长的对象所占的区域,叫作老年代。

年轻代

(1)年轻代分为:一个Eden区,两个Survivor区(称为S0和S1)。
(2)当 Eden 区第一次满的时候,会进行垃圾回收。首先将 Eden区的垃圾对象回收清除,并将存活的对象复制到 S0,此时 S1是空的。
(3)下一次 Eden 区满时,再执行一次垃圾回收。此次会将 Eden和 S0区中所有垃圾对象清除,并将存活对象复制到 S1,此时 S0变为空。
(4)如此反复在 S0 和 S1之间切换几次(默认 15 次)之后,如果还有存活对象。说明这些对象的生命周期较长,则将它们转移到老年代中。
(5)在这个过程中,总会有一个 Survivor 分区是空置的。Eden、from、to 的默认比例是 8:1:1,所以只会造成 10% 的空间浪费。
(6)使用复制算法进行垃圾回收。

老年代

(1)一个对象如果在新生代存活了足够长的时间而没有被清理掉,则会被复制到老年代。老年代的内存大小一般比新生代大,能存放更多的对象。如果对象比较大(比如长字符串或者大数组),并且新生代的剩余空间不足,则这个大对象会直接被分配到老年代上。
(2)注意:对于老年代可能存在这么一种情况,老年代中的对象有时候会引用到新生代对象。这时如果要执行新生代 GC,则可能需要查询整个老年代上可能存在引用新生代的情况,这显然是低效的。所以,老年代中维护了一个 512 byte 的 card table,所有老年代对象引用新生代对象的信息都记录在这里。每当新生代发生 GC 时,只需要检查这个 card table 即可,大大提高了性能。
(3)使用标记-压缩算法进行垃圾回收

java种的四种引用

引用 GC回收时机 使用示例
强引用 如果一个对象具有强引用,那垃圾回收器绝不会回收它 Object obj = new Object();
软引用 内存实在不足时,会对软引用进行回收
弱引用
虚引用

你可能感兴趣的:(java)