Java内存管理和垃圾回收

内存结构首先要知道java运行时内存结构,如下图。运行时内存包括方法区(Hotspot中又称“永久代”),堆,虚拟机栈,本地方法栈和程序计数器。 方法区和堆都是线程共享,方法区用于存放被虚拟机加...

内存结构

首先要知道java运行时内存结构,如下图。运行时内存包括方法区(Hotspot中又称“永久代”),堆,虚拟机栈,本地方法栈和程序计数器。

 

 

 

 

 

 

 

 

 

方法区和堆都是线程共享,方法区用于存放被虚拟机加载的类,常量,静态变量; 在大量使用反射,动态代理,ClassLoader的场景下,要考虑永久代的回收。堆主要是用于分配实例对象和数组,内存管理和垃圾回收基本都是针对堆的。

栈是存放局部变量的,本地方法栈是存非java的本地方法变量。

程序计数器是当前线程所执行的字节码的行号指示器,记录执行情况(唯一不会发生oom,out of memory)。

对象访问方式,有句柄,可以屏蔽对象实际地址的改变(gc时对象地址经常会改变);也可以直接访问数据(如Hotspot),优势速度快。

垃圾回收

垃圾判定的方法包括引用计数法和可达性分析法(GC Roots Tracing)。前者难解决重复引用问题,jvm采用的是后者。具体是选取一系列GC Roots对象,只保留从root对象可达的对象(利用OopMap,Ordinary Object Pointer记录了栈上和寄存器里哪些位置是引用),其余都是垃圾。

Java中的GC roots包含,

  • JavaStack中的引用的对象
  • 方法区中静态引用指向的对象
  • 方法区中常量引用指向的对象
  • Native方法中JNI引用的对象

垃圾回收算法

  • 标记清除算法
    清除算法分成2个阶段--标记和清除; 标记阶段对所有存活的阶段进行标记,标记完成后,再扫描整个空间未标记对象,直接回收不存活的对象.
    优点:大多数情况下比较高效,缺点是会造成内存碎片(不可用的空闲内存,具体指外部内存),碎片太多导致后面过程中对大内存的分配无足够空间时而提前猝发一次垃圾回收动作;
  • 复制算法
    将可用内存将容量划分成大小相等的2块,每次清理时将其中A内存还存活的对象复制到B内存里面,然后再把A中清理掉;
    优点高效且并不产生碎片,缺点牺牲了一半的内存为代价
    适用存活对象少,回收对象多
  • 标记整理算法
    该算法标记阶段和标记清除算法一样,完成标记后它不是直接清理可回收对象,而是将存活对象都向一端移动最后清理掉端边界意外的内存;
    适用于存活对象多,回收对象少的情况
  • 分代收集算法
    整合了复制算法和标记整理算法,根据新生代和老年代的不同特性采取上面的不同算法
    新生代 生命周期短,每次回收时都有大量垃圾对象需要回收 复制算法
    老年代 每次只有少量的对象需要回收 标记整理算法

垃圾收集器

Serial/Serial Old

最古老的收集器,是一个单线程收集器,用它进行垃圾回收时,必须暂停所有用户线程。Serial是针对新生代的收集器,采用Copying算法;而Serial Old是针对老生代的收集器,采用Mark-Compact算法。优点是简单高效,缺点是需要暂停用户线程。

ParNew

Seral/Serial Old的多线程版本,使用多个线程进行垃圾收集。

Parallel Scavenge

新生代的并行收集器,回收期间不需要暂停其他线程,采用Copying算法。该收集器与前两个收集器不同,主要为了达到一个可控的吞吐量(吞吐量= 用户线程时间/(用户线程时间+GC线程时间))。server级别默认采用的GC方式。

Parallel Old

Parallel Scavenge的老生代版本,采用Mark-Compact算法和多线程。

 CMS

Current Mark Sweep收集器是一种以最小回收时间停顿为目标的并发回收器,cpu占用比较高,因而采用Mark-Sweep算法。

G1

G1(Garbage First)收集器技术的前沿成果,是面向服务端的收集器,能充分利用CPU和多核环境。是一款并行与并发收集器,它能够建立可预测的停顿时间模型。

本文标题为:Java内存管理和垃圾回收