Java之GC

来源:互联网 发布:怎么开淘宝天猫店 编辑:程序博客网 时间:2024/05/08 20:14

内存结构

JVM在启动后内存分成如下几个区域:

线程私有部分:

  • PC, Program Counter Register, 当前执行指令地址,用于线程切换后恢复
  • JVM Stack, 调用方法时存放栈帧,局部变量存放地,栈帧大小在编译器就确定,记录在class文件中method_info的属性表中,Stack默认大小为1M,可通过启动参数-Xss或new Thread时传递参数指定;栈内部不足时会抛StackOverFlowException, jstack pid可看出线程栈
  • Native Method Stack,本地方法区,调用Native方法的Stack, HotSpot将其合并到了JVM Stack中

线程共享部分:

  • Heap, 堆区,空间最大的区域,new Object对存放到此处,通过-Xms指定最小值,-Xmx指定最大值
  • Method Area,方法区,存放类加载后的元信息即Class Object,方法区包含运行时常量池,其中存储各种基本数据类型及String类型的常量以及类方法、字段的符号引用,方法区默认64M,-XX:PermSize指定大小,-XX:MaxPermSize指定最大值

线程共享部分在多线程环境下需保证线程安全,当此部分内存不足时会抛OutOfMemoryException,GC也是在此部分上进行。

ThreadLocal数据在Heap区,每个线程在Heap区分配一块地方。

GC

GC作用在Heap区和Method Aera, GC 线程是守护线程,自动回收不再存活对象占用内存,为便于GC, 将线程共享部分划分为:

Heap区划分为Young Generation和Old Generation:

  • Young Generation,新生代,对象从新生代消失为Minor/Young GC
  • Old Generation, 老年代,对象从老年代消失为Major/Full GC

Method Area对应永久代:

  • Permanet Generation,永久代GC也是Major GC

对不通的分代,根据数据生命周期不同,采用的算法也不同。

GC介绍可参看:http://www.importnew.com/1993.html

Minor GC

Minor GC运行在新生代,新生代被划分成三个部分:

  • Eden
  • Survivor
    • Survivor1(S#1)
    • Survivor2(S#2)

两个Survivor是因为在新生代进行GC时采用了复制算法,将一个Survivor区直接copy至另外一个,两倍空间,速度快,无内存碎片,此种算法适用于频繁创建、生命周期短的对象。

算法步骤:

  1. 绝大多数刚刚被创建的对象会存放在Eden
  2. Eden满后执行了一次GC,存活的对象被移动到其中一个Survivor
  3. 此后,在Eden执行GC之后,存活的对象会被堆积在同一个Survivor
  4. 当一个Survivor饱和,还在存活的对象会被移动到另一个Survivor,然后清空已经饱和的那个Survivor,同一时刻只有一个Survivor起作用,另一个Survivor为空

经过多次Young GC, Survivor中达到一定年龄的对象会进入老年代,对象年龄使用经过GC次数表示,记录在对象头中。

大多数对象生命周期较短,会在Young Generation消失,因此Minor GC发生频率较高,注重时间效率,空间换时间,采用复制-回收算法

Major GC

Major GC会发生在Old Generation和Permanent Generation,一般是在老年代或永久代空间满时发生,相对Youg GC执行频率低,一次执行时间久,因为存储的是大对象,且Old Generation空间一般比Young Generation大。

Major GC一般采用Mark-Sweep-Compact,即标记存活对象,清理无用对象,此时会产生内存碎片,压缩碎片整理,将存活对象聚集在一起,此种算法时间稍长,因此适用于生命周期长的对象。

Major GC具体执行的过程由配置的算法决定,任何一种算法都会导致SWT(Stop-The-Word),目前JDK提供以下几种算法:

  1. Serial GC,-XX:+UseSerialGC
  2. Parallel GC, -XX:+UseParallelGC
  3. Parallel Old GC (Parallel Compacting GC), -XX:+UseParallelOldGC
  4. Concurrent Mark & Sweep GC (or “CMS”),-XX:+UseConcMarkSweepGC
  5. Garbage First (G1) GC

目前一般采用CMS算法,CMS是一种低延迟GC,SWT时间很短,但是需要占用更多内存和CPU,默认不支持压缩。

标记垃圾对象

查找垃圾对象主要有两种方式:

  • 引用计数法

    在一个对象上存储其被引用次数,如果为0则是废弃对象,缺点是无法检测循环引用(A->B->A)

  • 根搜索法

    从GC Root集合作为起点向下搜索,可达对象为存活对象,不可达对象则为垃圾对象。GC Root一般由Method Area、JVM Stack中引用组成,HotSpot使用跟搜索算法。

GC配置

一般会根据实际物理资源配置JVM启动参数决定内存各个分区的大小,当然也可采用默认配置,启动配置参数见上图。

JVM启动后可以通过jinfo pid 查看启动参数,也可通过jmap -heap pid查看实际分配的各部分比例大小。

这里有一份关键业务系统JVM参数推荐,当然也包含GC相关:

http://calvin1978.blogcn.com/articles/jvmoption-2.html

GC监控

GC可能会影响程序性能,因此需要关注GC具体执行情况,两种方式可监控GC执行情况:

  • 记录GC明细
  • 统计GC次数及时间

记录GC明细

GC是GC线程的行为,因此可在执行时通过日志形式记录GC明细, 在JVM启动参数中添加:

-verbos:gc-Xloggc:/xxx/gc.log-XX:+PrintGCDetails-XX:+PrintGCTimeStamps-XX:+PrintHeapAtGC-XX:+PrintGCDateStamps

统计GC次数及时间

jstat可统计GC执行情况,当然jstat也是根据GC执行时记录的明细汇总得到的, jstat -gc pid frequency count 会显示各分区的大小、使用情况以及GC执行次数和总时间。

GC监控参考:http://www.importnew.com/2057.html

GC调优

GC优化永远是最后一件事情,只有真正确认GC有问题才进行优化,盲目优化可能适得其反。以下文章详细介绍了何时该进行GC优化以及如何进行GC优化:

http://www.importnew.com/3146.html

原创粉丝点击