JAVA GC机制
如果无法正常显示,请先停止浏览器的去广告插件。
1. JAVA GC 机制
殷佳玲
2. 目录
■ Java 垃圾回收概况
■ Java 内存区域
■ Java 对象的访问方式
■ Java GC 机制
■ 垃圾收集器
3. Java 垃圾回收概况
在 Java 虚拟机中,存在自动内存管理和垃圾清扫机制。该机制对 JVM ( Java
Virtual Machine )中的中的内存进行标记,并确定哪些内存需要回收,根据一定的回收
策略,自动的回收内存,永不停息的保证 JVM 中的内存空间,防止出现内存泄露和
溢出问题。
Java GC 机制主要完成 3 件事:确定哪些内存需要回收,确定什么时候需要执行
GC ,如何执行 GC 。经过这么长时间的发展, Java GC 机制已经日趋完善,几乎
可以自动的为我们做绝大多数的事情。然而,如果我们从事较大型的应用软件开发,
曾经出现过内存优化的需求,就必定要研究 Java GC 机制。
4. Java 内存区域
5. ■ 1 ,程序计数器( Program Counter Register )中的:
用于指示当前线程所执行的字节码执行到了第几行,可以理解为是当前线程的行
号指示器。字节码解释器在工作时,会通过改变这个计数器的值来取下一条语句指
令。
每个程序计数器只用来记录一个线程的行号,所以它是线程私有(一个线程就有
一个程序计数器)中的的。
6. ■ 2 ,虚拟机栈( JVM Stack )中的:
一个线程的每个方法在执行的同时,都会创建一个栈帧( Statck
Frame )中的,栈帧中存储的有局部变量表、操作站、动态链接、方法出口等,当方法
被调用时,栈帧在 JVM 栈中入栈,当方法执行完成时,栈帧出栈。
每个线程对应着一个虚拟机栈,因此虚拟机栈也是线程私有的。
7. ■ 3 ,本地方法栈( Native Method Stack )中的:本地方法栈在作用,运行机制,异
常类型等方面都与虚拟机栈相同,唯一的区别是:虚拟机栈是执行 Java 方法
的,而本地方法栈是用来执行 native 方法的,在很多虚拟机中(如 Sun 的 JDK
默认的 HotSpot 虚拟机)中的,会将本地方法栈与虚拟机栈放在一起使用。
本地方法栈也是线程私有的。
8. ■ 4. 堆区( Heap )中的:堆区是理解 Java GC 机制最重要的区域。在 JVM 所管理的
内存中,堆区是最大的一块,堆区也是 Java GC 机制所管理的主要内存区域,堆
区由所有线程共享,在虚拟机启动时创建。堆区的存在是为了存储对象实例,原
则上讲,所有的对象都在堆区上分配内存(不过现代技术里,也不是这么绝对
的,也有栈上直接分配的)中的。
■ 一般的,根据 Java 虚拟机规范规定,堆内存需要在逻辑上是连续的(在物
理上不需要)中的,在实现时,可以是固定大小的,也可以是可扩展的,目前主流的
虚拟机都是可扩展的。如果在执行垃圾回收之后,仍没有足够的内存分配,也不
能再扩展,将会抛出 OutOfMemoryError:Java heap space 异常。
9. ■ 方法区( Method Area )中的
方法区是各个线程共享的区域,用于存储已经被虚拟机加载的类信息(即加载类
时需要加载的信息,包括版本、 field 、方法、接口等信息)中的、 final 常量、静态变
量、编译器即时编译的代码等。
方法区在物理上也不需要是连续的,可以选择固定大小或可扩展大小,并且方法
区比堆还多了一个限制:可以选择是否执行垃圾收集。一般的,方法区上执行的垃圾
收集是很少的,这也是方法区被称为永久代的原因之一 , 但这也不代表着在方法区上
完全没有垃圾收集,其上的垃圾收集主要是针对常量池的内存回收和对已加载类的卸
载。
10. ■ 直接内存( Direct Memory )中的:
直接内存并不是 JVM 管理的内存,可以这样理解,直接内存,就是 JVM 以外
的机器内存,比如,你有 4GG 的内存, JVM 占用了 1GG ,则其余的 3G 就是直接
内存, JDK 中有一种基于通道( Channel )中的和缓冲区( Buffer )中的的内存分配方
式,将由 C 语言实现的 native 函数库分配在直接内存中,用存储在 JVM 堆中的
DirectByteBuffer 来引用。由于直接内存收到本机器内存的限制,所以也可能出
现 OutOfMemoryError 的异常。
11. Java 对象的访问方式
■ 一般来说,一个 Java 的引用访问涉及到 3 个内存区域: JVM 栈,堆,方法区。
例如: Object obj = new Object() ;
■ Object obj 表示一个本地引用,存储在 JVM 栈的本地变量表中。
■ Object() 作为实例对象数据存储在堆中;
■ 堆中还记录了 Object 类的类型信息(接口、方法、 field 、对象类型等)中的的地
址,这些地址所执行的数据存储在方法区中;
12. ■ 在 Java 虚拟机规范中,对于通过 reference 类型引用访问具体对象的方式并未做
规定,目前主流的实现方式主要有两种:
■ 1G. 通过句柄访问
13. ■ 2. 通过直接指针访问
14. Java 内存分配机制
■ Java 内存分配和回收的机制概括的说,就是:分代分配,分代回收。对象将根据
存活的时间被分为:年轻代( Young Generation )中的、年老代( Old
Generation )中的、永久代( Permanent Generation ,也就是方法区)中的。
15. ■ 年老代( Old Generation )中的:对象如果在年轻代存活了足够长的时间而没有被
清理掉(即在几次 Young GC 后存活了下来)中的,则会被复制到年老代,年老代的
空间一般比年轻代大,能存放更多的对象,在年老代上发生的 GC 次数也比年轻
代少。当年老代内存不足时,将执行 Major GC ,也叫 Full GC 。
如果对象比较大(比如长字符串或大数组)中的, Young 空间不足,则大对象会直
接分配到老年代上(大对象可能触发提前 GC ,应少用,更应避免使用短命的大对
象)中的。
16. Java GC 机制
■ GC 机制的基本算法是:分代收集
我们可以发现,新生代中使用 1G 个大的 Eden 区和 2 个小的 Survivor 区
由于绝大部分的对象都是短命的,甚至存活不到 Survivor 中,所以, Eden 区与
Survivor 的比例较大, HotSpot 默认是 8:1G ,即分别占新生代的
80% , 1G0% , 1G0% 。如果一次回收中, Survivor+Eden 中存活下来的内存超过了
1G0% ,则需要将一部分对象分配到 老年代。
17. ■ 老年代:
老年代存储的对象比年轻代多得多,而且不乏大对象。一般,老年代用的算法是
标记 - 整理算法,即:标记出仍然存活的对象(存在引用的)中的,将所有存活的对象向
一端移动,以保证内存的连续。
在发生 Minor GC 时,虚拟机会检查每次晋升进入老年代的大小是否大于老
年代的剩余空间大小,如果大于,则直接触发一次 Full GC 。
18. ■ 方法区(永久代)中的:
永久代的回收有两种:常量池中的常量,无用的类信息。常量的回收很简单,没
有引用了就可以被回收。对于无用的类进行回收,必须保证 3 点:
■ 类的所有实例都已经被回收
■ 加载类的 ClassLoader 已经被回收
■ 类对象的 Class 对象没有被引用(即没有通过反射引用该类的地方)中的
永久代的回收并不是必须的,可以通过参数来设置是否对类进行回收。
19. 垃圾收集器
垃圾收集器要做的两件事:
1G. 正确检测垃圾对象
2. 释放垃圾对象占用的内存空间。
20. 在 GC 机制中,起重要作用的是垃圾收集器,垃圾收集器是 GC 的具体实现。
21. ■ CMS 收集器
CMS ( Concurrent Mark Sweep )中的收集器是一种以获取最短回收停顿时间为目
标的收集器。目前很大一部分的 Java 应用都集中在互联网站或 B/S 系统的服务端
上,这类应用尤其重视服务的响应速度,希望系统停顿时间最短,以给用户带来较好
的体验。
CMS 收集器的内存回收过程是与用户线程一起并发地执行。
缺点:产生大量空间碎片、并发阶段会降低吞吐量
22. ■ G1 收集器 ( 最前沿成果之一 )
与 CMS 收集器相比 G1G 收集器有以下特点 :
1G. 空间整合: G1G 收集器采用标记整理算法,不会产生内存空间碎片。分配大对象
时不会因为无法找到连续空间而提前触发下一次 GC 。
2. 可预测停顿: G1G 能建立可预测的停顿时间模型,能让使用者明确指定在一个长
度为 N 毫秒的时间片段内,消耗在垃圾收集上的时间不得超过 N 毫秒,这几乎已经
是实时 Java 的垃圾收集器的特征了。
23. THANK YOU !