JVM-垃圾回收

1 如何辨别一个对象已死亡

1.1 引用计数法与可达性分析

1.1.1 引用计数法

概述

  • 为每个对象添加一个计数器,当该对象被一个变量引用时,计数器就会加一.当指向该对象的引用被赋了其他的值,对象的引用就会减一.

缺点:

  • 需要额外的空间储存计数器.
  • 不能处理循环引用,回造成内存泄露.

1.1.2 可达性分析法-java虚拟机主流算法

概述

  • 将一系列GC Roots作为初始存活对象的集合.从该集合出发,所有能被该集合引用到的对象都加入这个集合.这个过程被称为标记.所有未被标记的都认为是不可达的,是可以被回收的.

GC Root,包括但不限于:

  • java 方法栈帧中的局部变量
  • 已加载类的静态变量
  • JNI handles;
  • 已启动且未停止的线程.

缺点

  • 多线程情况下,其他线程可能会更新已经访问过的对象中的引用,从而导致漏报或者误报.

如何解决误报

  • stop the world ,即在垃圾回收时停止其他一切非垃圾回收的活动.回收的时机为当所有的线程都到达了安全点.
  • 线程的几种状态

    本地代码:当 Java 程序通过 JNI 执行本地代码时,如果这段代码不访问 Java 对象、调用 Java 方法或者返回至原 Java 方法,那么 Java 虚拟机的堆栈不会发生改变,也就代表着这段本地代码可以作为同一个安全点。

解释执行字节码:执行一条字节码就进行一次安全点检测.

执行即时编译器生成的机器码:在编译机器码时插入安全检查点.在生成代码的出口以及非计数循环的循环回边插入.

线程阻塞:安全

2 垃圾回收方法

2.1 清除

把死亡对象占据的内存标记为空闲对象,并记录在一个空闲列表之中.要新建对象时,就把这些空间分配给新的对象.

  • 缺点,会造成内存碎片.

    2.2 压缩

    把存活的对象聚集到内存区域的起始位置.
  • 缺点,压缩算法的开销.

    2.3 复制

    把内存区域分成两份,分别用两个指针from, to 来处理.当发生对象回收时,将存活的对象从from复制到to的区域,然后交换from和to的指针.
  • 缺点,堆空间使用效率低下.

3 java虚拟机的划分

java虚拟机垃圾分代基于的基本假设-大部分对象只存活一小段时间,小部分对象存活很长时间.

  • 新生代-老年代
  • 新生代又分为Eden区和两个大小相同的Survivor区

  • 当调用new指令时,会在Eden划分出一块区域.当Eden区域内存满了,Java 会出发一次Minor GC收集新生代垃圾.

  • 生存下来的对象会被送到Survivor区.
  • 发生Minor GC时,Survivor区的存活对象也会被从from移到to区.并交换from和to的指针.
  • Survivor区的对象被来回复制超过15次以后,就会被送到老年代.
  • 如果单个Survivor区的内存使用量超过50%,较高复制次数的对象也会被晋升至老年代.
  • TLAB(Thread Local Allocation Buffer)-这个技术是用于解决多线程竞争堆内存分配问题的,核心原理是对分配一些连续的内存空间
  • 如果老年代的对象引用了新生代的对象,那么这个引用也会被加入到GC Roots中.这会导致全堆扫描
  • 如何应对全堆扫描-卡表,大致地标出可能存在老年代到新生代引用的内存区域。
posted @ 2019-07-08 19:19  一把水果刀  阅读(234)  评论(0编辑  收藏  举报