Java虚拟机内存模型及垃圾回收监控调优

Java虚拟机内存模型及垃圾回收监控调优

如果你想理解Java垃圾回收如果工作,那么理解JVM的内存模型就显的非常重要。今天我们就来看看JVM内存的各不同部分及如果监控和实现垃圾回收调优。

JVM内存模型        

正如你上图所看到的,JVM内存可以划分为不同的部分,广义上,JVM堆内存可以划分为两部分:年轻代和老年代(Young
Generation and Old
Generation)

年轻代(Young
Generation)

年轻代用于存放由new所生成的对象。当年轻代空间满时,垃圾回收就会执行。这个垃圾回收我们称之为
最小化垃圾回收(Minor
GC)。年轻代又可以分为三分部: 一个Eden内存区(Eden
Memory)和两个Survivor 内存区(Survivor
Memory)。

 

关于年轻代的重点:

  • 大多数新创建的对象都存放在Eden内存区。

  • 当Eden区存满对象时,最小化GC将会被执行,所有存活的对象被移到其中一个Survivor区。

  • 最小化GC同时也会检查存活的对象并将它们移到另一个Survivor区,所以在一段时间,其中一个Survivor区总是空的。

  • 存活的对象经过多次GC后,将会被移到老年代(Old
    Generation)通常年轻代转化多长时间变为老年代都会设置一个阀值。

老年代(Old
Generation)

老年代内存包含那些经过多次最小化GC且存活的对象。
通常当老年代内存满时垃圾回收会被执行。我们称之为大GC
(Major GC),通常这个过程会花费很长的时间。

Stop the World
Event

所有的垃圾回收都是“阻塞”事件(“Stop
the World” events) ,因为所有应用程序线程必须停止,直到垃圾回收操作完成之后才能继续。

由于年轻代总是保存着短期存活的对象,最小化GC非常快,应用程序也不会受到影响。然而大GC(Major
GC)由于它要检查所有存活的对象,所以需要花费很长的时间。
大GC会在垃圾回收期间使得你的应用程序没有任何响应,应最大化的减少此类GC。如果你有一个即时响应应用程序且总是有很多的大GC在执行。你会发现存在超时错误。

垃圾回器扫行的时间依赖于GC所使用的策略。这就是为什么对于那些高响应用程序来说,GC的监控与调优显得非常必要。

永久代(Permanent
Generation)

永久代或持久代包含JVM用来描述应用程序中使用的类和方法的元数据。注意 永久代不是JAVA堆内存的一部分。

JVM在运行期间依据应用程序所使用的类来存入永久代,同时也包含Java SE库类各方法。 永久代中的对象通过全GC(Full
GC)来进行垃圾回收。

方法区Method
Area

方法区是永久代的一部分,用于存储类结构(运行时的常量和静态变量)及方法和构造的代码。

运行时常量池Runtime
Constant Pool

运行时常量池是类中常量池在编译期的表示,它包含类的运行时常量,静态方法,它是方法区的一部分。

Java栈内存Java
Stack Memory

Java 栈内存用于执行线程。包含方法短期存活的特定值及方法中所涉及的指向堆中其它对象的引用。

Java
堆内存Switches(Java Heap Memory Switches)

提供了大量的内存Switch,我们可以用来设置内存大小及它们的比率。一此常用的内存Switches如下。



























VM Switch VM Switch 描述
-Xms 设置JVM启动时堆初始值。
-Xmx 设置堆的最大值
-Xmn 设置年轻代的大小
-XX:PermGen 设置永久代内存的初始值
-XX:MaxPermGen 设置永久代内存的最大值
-XX:SurvivorRatio 用于提供Eden区和Survivor区的比例, 例如,如果年轻代的大小为
10M,VM switch is -XX:SurvivorRatio=2 那么 5 M 分配给 Eden 区,每一个Survivor 区为
2.5 M .默认比例大小为  8。
-XX:NewRatio 老年代/新生代的比例. 默认值是 2.

大多数情况下,上述选项是非常有用的。如果你也想试试其它参数可以查看 JVM官方主页

JAVA
GC

Java
GC是从内存中标记、移除非可用对象并释放所分配的空间用于后续创建对像的过程。Java编程语言最大的功能之一就是自动垃圾回收。不像其它类似C的编程语言,它们的内存分配与回收可以人为操作。

GC是后台执行用于检查内存中的所有对象并找出未被任务程序所引用的对象。所有这些未引用的对象将被删除,同时空间也会被回收用于分配给其它对象。

 关于标记、删除方法有两个问题:

1.由于新创建的对象将会变成不可用,所有效率上不是很高。

2.多次GC后可用的对象很能在后续的GC后仍然可用。

上述方法的不足在于Java的GC是分代的,在堆内存中分年轻代和老年代。 前面我已经解释过基于最小化GC(Minor
GC)和大GC(Major GC)对象是如果被扫描并从一个分代区移到别一个分区。

Java
GC的
类型

在应用程序中我们可以使用如下五种类型的GC。我们可以使用JVM相关设置为应用程序开启GC策略,现在我们就来具体的看看.

  1. 串行GC (Serial
    GC -XX:+UseSerialGC):
    串行GC为年轻代和老年代GC提供简单的标记-清除-压缩方法。串行GC在独立应用程序运行的少量CPU的客户机器上非常有效。对于那些低内存占用的应用程序非常有利。

  2. 并行GC (Parallel
    GC -XX:+UseParallelGC):
    并行GC中大部分实现与串行GC相同,除了年轻代的垃圾回收使用N个线程,N是系统CPU内核的数量。我们可以通过 JVM
    的 -XX:ParallelGCThreads=n
    选项来控制线程的数量。并行的垃圾回收器因为使用多CPU来加速GC的实现所以也称为并行收集器(throughput
    collector).并行GC使用单线程来处理老年代的垃圾回收.

  3. 并行Old GC (Parallel
    Old
    GC  -XX:+UseParallelOldGC):此类型的GC中年轻代和老年代GC都使用多线程进行。其它与并行GC相同,

  4. 并发标记清除收集器(Concurrent
    Mark Sweep (CMS) Collector) (-XX:+UseConcMarkSweepGC):
    CMS收集器是并发短暂停收集器。它为老年代进行垃圾回收。CMS收集器 通过与应用程序线程并发执行垃圾回收
    从而缩短因GC而出现的暂停时间。CMS收集器在年轻代使用与并行收集器相同的算法。这类垃圾收集器适合不能容忍长时间暂停的响应程序。我们可以通过JVM中的 -XX:ParallelCMSThreads=n
    来设置CMS收集器中的线程数。

  5. G1 垃圾收集器G1 Garbage
    Collector (-XX:+UseG1GC) Java 7中可以使用G1 垃圾收集器。它的最终目标是替换掉CMS收集器。G1收集器是一个并行、并发、增量压缩、低暂停的垃圾收集器。

G1垃圾收集器不同于其它收集器,它没有年轻代、老年代空间,它将堆空间划分成多个相同大小的堆区域(heap
regions),当调用一次垃圾回收,它首先收集实时性不是很高的区域的数据,因此称为”Garbage
First”。你可以找到更详细的 Garbage-First Collector Oracle文档。

JAVA
垃圾收集监控

我们可以使用Java命令行和UI工具来监控应用程序的垃圾收集活动。下面的例子中,我使用Java SE Downloads
中一个演示程序。

如果你想使用同样的程序,前往 Java SE Downloads  页面下载JDK
7 and JavaFX Demos and
Samples
 我使用的和序示例是Java2Demo.jar 
可以在jdk1.7.0_55/demo/jfc/Java2D 目录中找到。当然了这步可选。你可以选用任何Java程序执行GC监控命令。

我使用的启动演示程序的命令是:





1

[email protected]:~/Downloads/jdk1.7.0_55/demo/jfc/Java2D$
java -Xmx120m -Xms30m -Xmn10m
-XX:PermSize=20m -XX:MaxPermSize=20m -XX:+UseSerialGC -jar
Java2Demo.jar

JSTAT

我们可以使用 jstat 命令行工具来监控JVM内存和GC活动。标准的JDK中含有此命令。因此可以直接使用。

运行 jstat 之前你需要知道程序的进程ID号。你可以运行 ps -eaf | grep
java 命令来获取。






1

2

3

[email protected]:~$ ps
-eaf | grep
Java2Demo.jar

501 9582 
11579   0  9:48PM ttys000    0:21.66

/usr/bin/java -Xmx120m -Xms30m -Xmn10m -XX:PermSize=20m
-XX:MaxPermSize=20m -XX:+UseG1GC -jar Java2Demo.jar

501 14073
14045   0  9:48PM ttys002    0:00.00

grep Java2Demo.jar

我的Java程序的进程ID号为 9582.现在我们可以如下执行jstat 命令。


1 [email protected]:~$ jstat -gc 9582 1000
2 S0C S1C S0U S1U EC EU OC OU PC PU YGC YGCT FGC FGCT GCT
3 1024.0 1024.0 0.0 0.0 8192.0 7933.3 42108.0 23401.3 20480.0 19990.9 157 0.274 40 1.381 1.654
4 1024.0 1024.0 0.0 0.0 8192.0 8026.5 42108.0 23401.3 20480.0 19990.9 157 0.274 40 1.381 1.654
5 1024.0 1024.0 0.0 0.0 8192.0 8030.0 42108.0 23401.3 20480.0 19990.9 157 0.274 40 1.381 1.654
6 1024.0 1024.0 0.0 0.0 8192.0 8122.2 42108.0 23401.3 20480.0 19990.9 157 0.274 40 1.381 1.654
7 1024.0 1024.0 0.0 0.0 8192.0 8171.2 42108.0 23401.3 20480.0 19990.9 157 0.274 40 1.381 1.654
8 1024.0 1024.0 48.7 0.0 8192.0 106.7 42108.0 23401.3 20480.0 19990.9 158 0.275 40 1.381 1.656
9 1024.0 1024.0 48.7 0.0 8192.0 145.8 42108.0 23401.3 20480.0 19990.9 158 0.275 40 1.381 1.656

jstat命令的最后一个参数是每次输出的时间间隔,因此它会每隔一秒打印内存及垃圾回收数据。下面详细看看每列。

    • S0C and S1C: 这列显示当前 Survivor0 and Survivor1
      区域的大小(KB)

    • S0U and S1U: 这列显示 Survivor0 and Survivor1
      区域的使用情况(KB). 其中一个Survivor区域总是空的。

    • EC and EU: 这列显示Eden区的当前大小及使用情况(KB). 注意
      EU 的大小逐渐增加,当达到EC大小, 最小化 GC 被调用 ,EU 的大小减小。

    • OC and OU:
      这列显示了老年代的当前大小及使用情况(KB)

    • PC and PU: 这列显示了 持久化代(Perm Gen)
      的当前大小及使用情况(KB)

    • YGC and YGCT: YGC 列显示年轻代发生GC事件的数量。 YGCT
      列显示年轻代发生GC操作累计时间.  注意这两列值都在增加与EU值减少是在同一行。这主要是最小化GC的原因。

    • FGC and FGCT: FGC 列显示了FUll GC发生的数量. FGCT 列显示了FULL
      GC操作的累计时间. 注意Full GC
      所用的时间相比年轻代GC所用的时间要大的多。

    • GCT:  这列显示了GC 操作总累计时间。 注意它是
      YGCT 和 FGCT 值的总和。

jstat的优点在于它可以在无GUI的远程服务器上执行。注意S0C, S1C
和EC之和为 10M ,与我们通过JVM选项 -Xmn10m 设置的值一样。

Java
VisualVM with Visual GC

如果你想在GUI下查看内存及GC操作。 那么你可以使用 jvisualvm
工具。Java VisualVM 也同样包含在JDK中,你不需要单独下载。

只需要运行在终端上执行jvisualvm 命令来启动Java
VisualVM程序。一旦启动,你需要通过Tools--》Plugins选项安装Visual
GC 
插件,正如下图所示。

   Visual GC 安装完毕后,左边列中打开程序前往Visual
GC 
部分。你将会得到如下图所示的JVM内存及GC截图。

Java GC
调优是提高应用程序吞吐量的最后选择,只有当你发现长时间的GC导致性能下降而产生应用程序超时。

你会在日志中看到java.lang.OutOfMemoryError:
PermGen space的错误信息。然后可以尝试监控并通过使用JVM 选项  -XX:PermGen
and -XX:MaxPermGen  来增加Perm Gen内存空间。你或许也可以尝试使用-XX:+CMSClassUnloadingEnabled
来检查使用CMS垃圾收集的性能如何?

如果你发现大量的FUll GC操作,你可以试着增加老年代内存空间。

总之GC调优需要花费大量的精力和时间,这里绝没有什么硬性或者快速的规则。你需要尝试不同的选项,比较他们,并找对你应用程序来说最好的那个。

这就是所有有关Java内存模型和垃圾回收。我希望这有助于你理解JVM内存模型及垃圾回收的过程。谢谢。

原文链接: JournalDev 翻译: TonySpark
译文链接: http://www.cnblogs.com/tonyspark/p/3731696.html

转载请保留原文出处、译者和译文链接。]

Java虚拟机内存模型及垃圾回收监控调优,布布扣,bubuko.com

时间: 2024-10-09 15:59:44

Java虚拟机内存模型及垃圾回收监控调优的相关文章

探秘Java虚拟机——内存管理与垃圾回收

详见:http://blog.yemou.net/article/query/info/tytfjhfascvhzxcyt106 1.Java虚拟机运行时的数据区 2.常用的内存区域调节参数 -Xms:初始堆大小,默认为物理内存的1/64(<1GB):默认(MinHeapFreeRatio参数可以调整)空余堆内存小于40%时,JVM就会增大堆直到-Xmx的最大限制 -Xmx:最大堆大小,默认(MaxHeapFreeRatio参数可以调整)空余堆内存大于70%时,JVM会减少堆直到 -Xms的最小

探秘Java虚拟机——内存管理与垃圾回收(转)

本文主要是基于Sun JDK 1.6 Garbage Collector(作者:毕玄)的整理与总结,原文请读者在网上搜索. 1.Java虚拟机运行时的数据区 2.常用的内存区域调节参数 -Xms:初始堆大小,默认为物理内存的1/64(<1GB):默认(MinHeapFreeRatio参数可以调整)空余堆内存小于40%时,JVM就会增大堆直到-Xmx的最大限制 -Xmx:最大堆大小,默认(MaxHeapFreeRatio参数可以调整)空余堆内存大于70%时,JVM会减少堆直到 -Xms的最小限制

java虚拟机——内存管理与垃圾回收机制

一  java内存区域与内存溢出异常(OOM) 1.运行时数据区域划分  

JVM内存模型以及垃圾回收

原文:http://www.blogjava.net/freeman1984/archive/2011/03/08/345929.html JVM内存模型以及垃圾回收内存由 Perm 和 Heap 组成. 其中 Heap = {Old + NEW = { Eden , from, to } }具体可查看javavisualvm JVM内存模型中分两大块,一块是 NEW Generation, 另一块是Old Generation. 在New Generation中,有一个叫Eden的空间,主要是

JAVA当中内存管理与垃圾回收!

很多Java面试的时候,都会问到有关Java垃圾回收的问题,提到垃圾回收肯定要涉及到JVM内存管理机制,Java语言的执行效率一直被C.C++程序员所嘲笑,其实,事实就是这样,Java在执行效率方面确实很低,一方面,Java语言采用面向对象思想,这也决定了其必然是开发效率高,执行效率低.另一方面,Java语言对程序员做了一个美好的承诺:程序员无需去管理内存,因为JVM有垃圾回收(GC),会去自动进行垃圾回收. 其实不然: 1.垃圾回收并不会按照程序员的要求,随时进行GC. 2.垃圾回收并不会及时

jvm的stack和heap,JVM内存模型,垃圾回收策略,分代收集,增量收集(转)

深入Java虚拟机:JVM中的Stack和Heap(转自:http://www.cnblogs.com/laoyangHJ/archive/2011/08/17/gc-Stack.html) 在JVM中,内存分为两个部分,Stack(栈)和Heap(堆),这里,我们从JVM的内存管理原理的角度来认识Stack和Heap,并通过这些原理认清Java中静态方法和静态属性的问题. 一般,JVM的内存分为两部分:Stack和Heap. Stack(栈)是JVM的内存指令区.Stack管理很简单,push

JVM内存模型及垃圾回收机制

JVM内存模型1.栈Java栈是与每一个线程关联的,JVM在创建每一个线程的时候,会分配一定的栈空间给线程.存储局部变量.引用.方法.返回值等.StackOverflowError:如果在线程执行的过程中,栈空间不够用,那么JVM就会抛出此异常,这种情况一般是死递归造成的.2.堆 Java中堆是由所有的线程共享的一块内存区域,堆用来保存各种JAVA对象,比如数组,线程对象等. 2.1堆的分代JVM堆一般分为三个部分:Young:年轻代Young区被划分为三部分,Eden区和两个大小严格相同的Su

Java内存模型与垃圾回收

1.Java内存模型 Java虚拟机在执行程序时把它管理的内存分为若干数据区域,这些数据区域分布情况如下图所示: 程序计数器:一块较小内存区域,指向当前所执行的字节码.如果线程正在执行一个Java方法,这个计数器记录正在执行的虚拟机字节码指令的地址,如果执行的是Native方法,这个计算器值为空. Java虚拟机栈:线程私有的,其生命周期和线程一致,每个方法执行时都会创建一个栈帧用于存储局部变量表.操作数栈.动态链接.方法出口等信息. 本地方法栈:与虚拟机栈功能类似,只不过虚拟机栈为虚拟机执行J

JAVA内存模型及垃圾回收自我总结

本文为原创,根据<深入理解java虚拟机>和自己的一些理解进行整理,单纯和看其他人的博客感觉不如自己一点点的画和记录来的印象深刻. JAVA内存模型: 判断对象是否已死(可以回收)的算法 方法区(永久代)回收的相关说明: 永久代的垃圾回收主要两部分内容:废弃的常量和无用的类. 废弃的常量:无任何对象引用此常量. 无用的类:1.该类所有实例已经被回收(堆中不存在该类的任何实例). 2.加载该类的ClassLoader已经被回收. 3.给类对应的Class对象没有任何地方呗引用,无法在任何地方通过