[关闭]
@ninjaz 2018-05-13T07:32:01.000000Z 字数 4998 阅读 28

垃圾收集器与内存分配策略

GC

  • 哪里需要回收?
  • 什么时候回收?
  • 如何回收?

线程隔离的内存区域(程序计数器、虚拟机栈、本地方法栈),线程结束时内存回收,无需多虑回收问题。


判断对象是否存活方法

  1. 引用计数
    对于一对象,每当有一地方引用,计数器+1;引用失效,计数器-1。
    JVM未使用该方法,没法解决对象之间互相循环引用的问题。
  2. 可达性分析

    GCRoots到这个对象不可达时,此对象需要回收

    • GC Roots对象作为起始点
      1. 虚拟机栈(栈帧中的本地变量表)中引用的对象。
      2. 方法区中类静态属性引用的对象。
      3. 方法区中常量引用的对象。
      4. 本地方法栈中JNI(即一般说的Native方法)引用的对象。
    • Reference Chain引用链 搜索走过的路径
      下图中的对象都存活
GC RootsObject1Object2Object3Object4

引用

两次标记过程

不可达对象不一定回收,回收的对象至少经历两次标记过程

  1. 不可达对象被第一次标记,并进行筛选:当对象没有覆盖finalize()或者finalize()方法已被虚拟机调用过,则视为“没有必要执行”
  2. 若此对象被判为有必要执行finalize()方法,则放入F-Queue队列中,稍后虚拟机建立低优先级的Finalizer线程去执行它(并不承诺等待它运行结束,避免执行缓慢和死循环的问题)。finalize()方法是对象自我救赎的最后机会。执行后只需和引用链上的任何对象关联即可。
  1. /**
  2. * 此代码演示了两点:
  3. * 1.对象可以在被GC时自我拯救。
  4. * 2.自救机会只有一次,因一个对象的finalize()方法最多只会被系统自动调用一次
  5. */
  6. public class FinalizeEscapeGC {
  7. public static FinalizeEscapeGC SAVE_HOOK = null;
  8. public void isAlive() {
  9. System.out.println("yes,i am still alive:)");
  10. }
  11. @Override
  12. protected void finalize() throws Throwable {
  13. super.finalize();
  14. System.out.println("finalize method executed!");
  15. FinalizeEscapeGC.SAVE_HOOK = this;
  16. }
  17. public static void main(String[] args) throws Throwable {
  18. SAVE_HOOK = new FinalizeEscapeGC();
  19. //对象第一次成功拯救自己
  20. SAVE_HOOK = null;
  21. System.gc();
  22. //因为finalize方法优先级很低,所以暂停0.5秒以等待它
  23. Thread.sleep(500);
  24. if (SAVE_HOOK != null) {
  25. SAVE_HOOK.isAlive();
  26. } else {
  27. System.out.println("no,i am dead:(");
  28. }
  29. //下面这段代码与上面的完全相同,但是这次自救却失败了
  30. SAVE_HOOK = null;
  31. System.gc();
  32. //因为finalize方法优先级很低,所以暂停0.5秒以等待它
  33. Thread.sleep(500);
  34. if (SAVE_HOOK != null) {
  35. SAVE_HOOK.isAlive();
  36. } else {
  37. System.out.println("no,i am dead:(");
  38. }
  39. }
  40. }

SAVE_HOOK对象的finalize()方法确实被GC收集器触发过,并且在被收集前成功逃脱了。但不建议使用该方法

回收方法区

方法区又称HotSpot的永久代;主要回收废弃常量和无用类

无用类需同时满足:
1. 该类所有实例已被回收(即Java堆中不存在该类实例)
2. 加载该类的ClassLoader已被回收
3. 该类对应的java.lang.Class对象没有在任何地方被引用,无法再任何地方用过反射访问该类的方法


垃圾收集算法

标记-清除法


复制算法

  • 新生代 一般被分为: 1 * Eden(较大) + 2 * Survivor(较小)
  • Eden : Survivor = 8 : 1
  • 由于内存不够,老年代一般不直接选用复制收集算法

标记-整理算法

一般老年代使用


分代收集算法

  • Java堆 分为 新生代 + 老年代
  • 新生代用复制算法;老年代用标记-清理or标记-整理

HotSpot的算法实现


垃圾收集器

如果说收集算法是内存回收的方法论,那么垃圾收集器就是内存回收的具体实现
并行:多条垃圾收集线程并行工作,但用户线程等待
并发:用户线程与垃圾线程同时执行
此处输入图片的描述


Serial


ParNew


Parallel Scavenge


Serial Old


Parallel Old


CMS


G1


内存分配与回收策略

  • 新生代GC(Minor GC):非常频繁,一般回收速度也比较快。
  • 老年代GC(Major GC/Full GC):经常会伴随至少一次的Minor GC(但非绝对的,在Parallel Scavenge收集器的收集策略里就有直接进行
    Major GC的策略选择过程)。 Major GC的速度一般会比Minor GC慢10倍以上。
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注