当前位置 : 首页 » 文章分类 :  开发  »  Java-JVM

Java-JVM

[TOC]


JVM内存模型

堆内存和非堆内存

堆(Heap)内存和非堆(Non-Heap)内存
按照官方的说法:“Java 虚拟机具有一个堆,堆是运行时数据区域,所有类实例和数组的内存均从此处分配。堆是在 Java 虚拟机启动时创建的。”“在JVM中堆之外的内存称为非堆内存(Non-heap memory)”。可以看出JVM主要管理两种类型的内存:堆和非堆。简单来说堆就是Java代码可及的内存,是留给开发人员使用的;非堆就是JVM留给 自己用的,所以方法区、JVM内部处理或优化所需的内存(如JIT编译后的代码缓存)、每个类结构(如运行时常数池、字段和方法数据)以及方法和构造方法 的代码都在非堆内存中

  • 堆内存
    JVM留给开发者用的内存。一般存放对象以及数组。
    JVM初始分配的堆内存由-Xms指定,默认是物理内存的1/64;JVM最大分配的堆内存由-Xmx指定,默认是物理内存的1/4。
    默认空余堆内存小于40%时,JVM就会增大堆内存直到-Xmx的最大限制;空余堆内存大于70%时,JVM会减少堆内存直到-Xms的最小限制。因此服务器一般设置-Xms、-Xmx相等以避免在每次GC后调整堆内存的大小。
    如果-Xmx不指定或者指定偏小,应用可能会导致java.lang.OutOfMemory错误,此错误来自JVM,不是Throwable的,无法用try…catch捕捉。

  • 非堆内存
    JVM留给自己用的内存。方法区、JVM内部处理或优化所需的内存(如JIT编译后的代码缓存)、每个类结构(如运行时常数池、字段和方法数据)以及方法和构造方法的代码都在非堆内存中。
    JVM初始分配的非堆内存由-XX:PermSize指定,默认是物理内存的1/64;JVM最大分配的非堆内存由-XX:MaxPermSize指定,默认是物理内存的1/4。(还有一说:MaxPermSize缺省值和-server -client选项相关,-server选项下默认MaxPermSize为64m,-client选项下默认MaxPermSize为32m)
    -XX:MaxPermSize设置过小会导致java.lang.OutOfMemoryError: PermGen space,原因如下:
    PermGen space用于存放Class和Meta的信息,GC不会对PermGen space进行处理,所以如果Load很多Class的话,就会出现上述Error。这种Error在web服务器对JSP进行pre compile的时候比较常见


分代收集

JVM里的GC(Garbage Collection)的算法有很多种,如标记清除收集器,压缩收集器,分代收集器等等,详见HotSpot VM GC 的种类。

现在比较常用的是分代收集(generational collection,也是SUN VM使用的,J2SE1.2之后引入),即将内存分为几个区域,将不同生命周期的对象放在不同区域里:young generation(年轻代,新生代),tenured generation(老年代)和permanet generation(永久代)。绝大部分的objec被分配在young generation(生命周期短),并且大部分的object在这里die。当young generation满了之后,将引发minor collection(YGC)。在minor collection后存活的object会被移动到tenured generation(生命周期比较长)。最后,tenured generation满之后触发major collection。major collection(Full gc)会触发整个heap的回收,包括回收young generation。permanet generation区域比较稳定,主要存放classloader信息。

新域、旧域以及永久域(有的也叫做新生代,年老代,和永久代)
JVM生成的所有新对象放在新域中。一旦对象经历了一定数量的垃圾收集循环后,便进入旧域。而在永久域中是用来存储JVM自己的反射对象的,如class和method对象,而且GC(GarbageCollection)不会在主程序运行期对永久域进行清理。
其中新域和旧域属于堆,永久域是一个独立域并且不认为是堆的一部分。

young generation有eden、2个survivor space组成。其中一个survivor区域一直是空的,是eden区域和另一个survivor区域在下一次copy collection后活着的objecy的目的地。object在survivor区域被复制直到转移到tenured区。

我们要尽量减少 Full gc 的次数(tenured generation 一般比较大,收集的时间较长,频繁的Full gc会导致应用的性能收到严重的影响)。

整个JVM内存大小=年轻代大小 + 年老代大小 + 持久代大小

堆内存GC
JVM(采用分代回收的策略),用较高的频率对年轻的对象(young generation)进行YGC,而对老对象(tenured generation)较少(tenured generation 满了后才进行)进行Full GC。这样就不需要每次GC都将内存中所有对象都检查一遍。

非堆内存不GC
GC不会在主程序运行期对PermGen Space进行清理,所以如果你的应用中有很多CLASS(特别是动态生成类,当然permgen space存放的内容不仅限于类)的话,就很可能出现PermGen Space错误。


合理选择GC策略

不管是YGC还是Full GC,GC过程中都会导致程序运行中中断,正确的选择不同的GC策略,调整JVM、GC的参数,可以极大的减少由于GC工作,而导致的程序运行中断方面的问题,进而适当的提高Java程序的工作效率。但是调整GC是以个极为复杂的过程,由于各个程序具备不同的特点,如:web和GUI程序就有很大区别(Web可以适当的停顿,但GUI停顿是客户无法接受的),而且由于跑在各个机器上的配置不同(主要cup个数,内存不同),所以使用的GC种类也会不同。


GC执行方式(collector种类)

jvm中GC执行的三种方式,即串行收集、并行收集、并发收集;
默认情况下,JDK5.0以前都是使用串行收集器,如果想使用其他收集器需要在启动时加入相应参数。JDK5.0以后,JVM会根据当前系统配置进行判断。

  • 串行收集(SerialGC),是jvm的默认GC方式,一般适用于小型应用和单处理器,算法比较简单,GC效率也较高,但可能会给应用带来停顿。
  • 并行收集(ParallelGC),是指GC运行时,对应用程序运行没有影响,GC和app两者的线程在并发执行,这样可以最大限度不影响app的运行。并行收集器主要以到达一定的吞吐量为目标,适用于科学技术和后台处理等。
  • 并发收集(ConcMarkSweepGC),是指多个线程并发执行GC,一般适用于多处理器系统中,可以提高GC的效率,但算法复杂,系统消耗较大。并发收集器主要是保证系统的响应时间,减少垃圾收集时的停顿时间。适用于应用服务器、电信领域等。

参考


JVM启动参数

JVM启动参数共分为三类:

  • 其一是标准参数-,所有的JVM实现都必须实现这些参数的功能,而且向后兼容;
  • 其二是非标准参数-X,默认jvm实现这些参数的功能,但是并不保证所有jvm实现都满足,且不保证向后兼容;
  • 其三是非Stable参数-XX,此类参数各个jvm实现会有所不同,将来可能会随时取消,需要慎重使用;

为什么需要参数调优?
JVM启动参数在一般开发中默认即可,不需要任何配置。但是在生产环境中,并不保证所有jvm实现都满足,所以为了提高性能,往往需要调整这些参数,以求系统达到最佳性能。另外,-X和-XX参数不保证向后兼容,也即是说“如有变更,恕不在后续版本的JDK通知”(这是官网上的原话);


标准参数

  • -Dproperty=value
    设置系统属性名/值对,运行在此jvm之上的应用程序可用System.getProperty("property")得到value的值。
    如果value中有空格,则需要用双引号将该值括起来,如-Dname=”space string”。
    该参数通常用于设置系统级全局变量值,如配置文件路径,以便该属性在程序中任何地方都可访问。

  • -X
    输出非标准的参数列表及其描述。

  • -client
    设置jvm使用client模式,特点是启动速度比较快,但运行时性能和内存管理效率不高,通常用于客户端应用程序或者PC应用开发和调试。

  • -server
    设置jvm使用server模式,特点是启动速度比较慢,但运行时性能和内存管理效率很高,适用于生产环境。在具有64位能力的jdk环境下将默认启用该模式,而忽略-client参数。


非标准参数

  • -Xms
    指定jvm堆的初始大小,默认为物理内存的1/64,最小为1M;可以指定单位,比如k、m、g,若不指定,则默认为单位字节。例如-Xms5120m,设置初始堆内存为5120M;-Xms2g,设置初始堆大小为2G。

  • -Xmx
    指定jvm堆的最大值,默认为物理内存的1/4或者1G,最小为2M;单位与-Xms一致。服务器一般设置-Xms、-Xmx相等以避免在每次GC后调整堆内存的大小。

  • -Xmn
    指定年轻代大小,此处的大小是(eden + 2 survivor space)。整个JVM内存大小=年轻代大小 + 年老代大小 + 持久代大小,增大年轻代后,将会减小年老代大小。

  • -Xss
    指定每个线程的堆大小。JDK5.0以后每个线程堆栈大小为1M。在相同物理内存下,减小这个值能生成更多的线程。但是操作系统对一个进程内的线程数还是有限制的,不能无限生成。一般小的应用, 如果栈不是很深, 应该是128k够用的 大的应用建议使用256k。这个选项对性能影响比较大,需要严格的测试。

  • -Xloggc:../log/gc.log
    与-verbose:gc功能类似,只是将每次GC事件的相关情况记录到一个文件中,文件的位置最好在本地,以避免网络的潜在问题。
    若与verbose命令同时出现在命令行中,则以-Xloggc为准。


非stable参数

用-XX作为前缀的参数列表在jvm中可能是不健壮的,SUN也不推荐使用,后续可能会在没有通知的情况下就直接取消了;但是由于这些参数中的确有很多是对我们很有用的,比如我们经常会见到的-XX:PermSize、-XX:MaxPermSize等等;
Java HotSpot VM中-XX:的可配置参数,这些参数可以被松散的聚合成三类:
行为参数(Behavioral Options):用于改变jvm的一些基础行为;
性能调优(Performance Tuning):用于jvm的性能调优;
调试参数(Debugging Options):一般用于打开跟踪、打印、输出等jvm参数,用于显示jvm更加详细的信息;

  • -XX:PermSize
    指定初始分配的非堆内存大小,即持久代(perm gen)初始大小,默认是物理内存的1/64

  • -XX:MaxPermSize
    指定最大非堆内存大小,即持久代最大值,默认是物理内存的1/4。例如-XX:MaxPermSize=256m,设置持久代大小为256M。

  • -XX:+UseSerialGC
    启用串行收集器

  • -XX:+UseParallelGC
    启用并行收集器,此配置仅对年轻代有效。即上述配置下,年轻代使用并发收集,而年老代仍旧使用串行收集(待验证)。

  • -XX:+UseParalledlOldGC
    启用并行年老代收集器

  • -XX:+UseConcMarkSweepGC
    对老生代采用并发标记交换算法进行GC

  • -XX:+PrintGC
    每次GC时打印相关信息
    输出形式:
    [GC 118250K->113543K(130112K), 0.0094143 secs]
    [Full GC 121376K->10414K(130112K), 0.0650971 secs]

  • -XX:+PrintGCTimeStamps
    打印每次GC的时间戳

  • -XX:+PrintHeapAtGC
    打印GC前后的详细堆栈信息

  • -XX:+HeapDumpOnOutOfMemoryError
    当首次遭遇OOM时导出此时堆中相关信息


参考


上一篇 Apache-httpd

下一篇 JBoss

阅读
3,206
阅读预计12分钟
创建日期 2016-10-18
修改日期 2017-07-18
类别
标签
百度推荐