jvm中堆栈以及内存区域分配


JVM内存组成结构 java

JVM栈由堆、栈、本地方法栈、方法区等部分组成,结构图以下所示: 算法

1)堆 数组

全部经过new建立的对象的内存都在堆中分配,其大小能够经过-Xmx和-Xms来控制。堆被划分为新生代和旧生代,新生代又被进一步划分为Eden和Survivor区,最后Survivor由From Space和To Space组成,结构图以下所示: 多线程

  • 新生代。新建的对象都是用新生代分配内存,Eden空间不足的时候,会把存活的对象转移到Survivor中,新生代大小能够由-Xmn来控制,也能够用-XX:SurvivorRatio来控制Eden和Survivor的比例
  • 旧生代。用于存放新生代中通过屡次垃圾回收仍然存活的对象

2)栈 并发

每一个线程执行每一个方法的时候都会在栈中申请一个栈帧,每一个栈帧包括局部变量区和操做数栈,用于存放这次方法调用过程当中的临时变量、参数和中间结果 分布式

3)本地方法栈 工具

用于支持native方法的执行,存储了每一个native方法调用的状态 学习

4)方法区 spa

存放了要加载的类信息、静态变量、final类型的常量、属性和方法信息。JVM用持久代(Permanet Generation)来存放方法区,可经过-XX:PermSize和-XX:MaxPermSize来指定最小值和最大值 操作系统

垃圾回收机制

JVM分别对新生代和旧生代采用不一样的垃圾回收机制

新生代的GC:

新生代一般存活时间较短,所以基于Copying算法来进行回收,所谓Copying算法就是扫描出存活的对象,并复制到一块新的彻底未使用的空间中,对应于新生代,就是在Eden和From Space或To Space之间copy。新生代采用空闲指针的方式来控制GC触发,指针保持最后一个分配的对象在新生代区间的位置,当有新的对象要分配内存时,用于检查空间是否足够,不够就触发GC。当连续分配对象时,对象会逐渐从eden到survivor,最后到旧生代,

用java visualVM来查看,能明显观察到新生代满了后,会把对象转移到旧生代,而后清空继续装载,当旧生代也满了后,就会报outofmemory的异常,以下图所示:

在执行机制上JVM提供了串行GC(Serial GC)、并行回收GC(Parallel Scavenge)和并行GC(ParNew)

1)串行GC

在整个扫描和复制过程采用单线程的方式来进行,适用于单CPU、新生代空间较小及对暂停时间要求不是很是高的应用上,是client级别默认的GC方式,能够经过-XX:+UseSerialGC来强制指定

2)并行回收GC

在整个扫描和复制过程采用多线程的方式来进行,适用于多CPU、对暂停时间要求较短的应用上,是server级别默认采用的GC方式,可用-XX:+UseParallelGC来强制指定,用-XX:ParallelGCThreads=4来指定线程数

3)并行GC

与旧生代的并发GC配合使用

旧生代的GC:

旧生代与新生代不一样,对象存活的时间比较长,比较稳定,所以采用标记(Mark)算法来进行回收,所谓标记就是扫描出存活的对象,而后再进行回收未被标记的对象,回收后对用空出的空间要么进行合并,要么标记出来便于下次进行分配,总之就是要减小内存碎片带来的效率损耗。在执行机制上JVM提供了串行GC(Serial MSC)、并行GC(parallel MSC)和并发GC(CMS),具体算法细节还有待进一步深刻研究。

以上各类GC机制是须要组合使用的,指定方式由下表所示:

指定方式

新生代GC方式

旧生代GC方式

-XX:+UseSerialGC

串行GC

串行GC

-XX:+UseParallelGC

并行回收GC

并行GC

-XX:+UseConeMarkSweepGC

并行GC

并发GC

-XX:+UseParNewGC

并行GC

串行GC

-XX:+UseParallelOldGC

并行回收GC

并行GC

-XX:+ UseConeMarkSweepGC

-XX:+UseParNewGC

串行GC

并发GC

不支持的组合

1-XX:+UseParNewGC -XX:+UseParallelOldGC

2-XX:+UseParNewGC -XX:+UseSerialGC

内存调优

首先须要注意的是在对JVM内存调优的时候不能只看操做系统级别Java进程所占用的内存,这个数值不能准确的反应堆内存的真实占用状况,由于GC事后这个值是不会变化的,所以内存调优的时候要更多地使用JDK提供的内存查看工具,好比JConsole和Java VisualVM。

对JVM内存的系统级的调优主要的目的是减小GC的频率和Full GC的次数,过多的GC和Full GC是会占用不少的系统资源(主要是CPU),影响系统的吞吐量。特别要关注Full GC,由于它会对整个堆进行整理,致使Full GC通常因为如下几种状况:

  • 旧生代空间不足
    调优时尽可能让对象在新生代GC时被回收、让对象在新生代多存活一段时间和不要建立过大的对象及数组避免直接在旧生代建立对象 
  • Pemanet Generation空间不足
    增大Perm Gen空间,避免太多静态对象 
  • 统计获得的GC后晋升到旧生代的平均大小大于旧生代剩余空间
    控制好新生代和旧生代的比例 
  • System.gc()被显示调用
    垃圾回收不要手动触发,尽可能依靠JVM自身的机制 

调优手段主要是经过控制堆内存的各个部分的比例和GC策略来实现,下面来看看各部分比例不良设置会致使什么后果

1)新生代设置太小

一是新生代GC次数很是频繁,增大系统消耗;二是致使大对象直接进入旧生代,占据了旧生代剩余空间,诱发Full GC

2)新生代设置过大

一是新生代设置过大会致使旧生代太小(堆总量必定),从而诱发Full GC;二是新生代GC耗时大幅度增长

通常说来新生代占整个堆1/3比较合适

3)Survivor设置太小

致使对象从eden直接到达旧生代,下降了在新生代的存活时间

4)Survivor设置过大

致使eden太小,增长了GC频率

另外,经过-XX:MaxTenuringThreshold=n来控制新生代存活时间,尽可能让对象在新生代被回收

由上一篇博文JVM学习笔记(三)------内存管理和垃圾回收可知新生代和旧生代都有多种GC策略和组合搭配,选择这些策略对于咱们这些开发人员是个难题,JVM提供两种较为简单的GC策略的设置方式

1)吞吐量优先

JVM以吞吐量为指标,自行选择相应的GC策略及控制新生代与旧生代的大小比例,来达到吞吐量指标。这个值可由-XX:GCTimeRatio=n来设置

2)暂停时间优先

JVM以暂停时间为指标,自行选择相应的GC策略及控制新生代与旧生代的大小比例,尽可能保证每次GC形成的应用中止时间都在指定的数值范围内完成。这个值可由-XX:MaxGCPauseRatio=n来设置

 

最后汇总一下JVM常见配置

  1. 堆设置
    • -Xms:初始堆大小
    • -Xmx:最大堆大小
    • -XX:NewSize=n:设置年轻代大小
    • -XX:NewRatio=n:设置年轻代和年老代的比值。如:为3,表示年轻代与年老代比值为1:3,年轻代占整个年轻代年老代和的1/4
    • -XX:SurvivorRatio=n:年轻代中Eden区与两个Survivor区的比值。注意Survivor区有两个。如:3,表示Eden:Survivor=3:2,一个Survivor区占整个年轻代的1/5
    • -XX:MaxPermSize=n:设置持久代大小
  2. 收集器设置
    • -XX:+UseSerialGC:设置串行收集器
    • -XX:+UseParallelGC:设置并行收集器
    • -XX:+UseParalledlOldGC:设置并行年老代收集器
    • -XX:+UseConcMarkSweepGC:设置并发收集器
  3. 垃圾回收统计信息
    • -XX:+PrintGC
    • -XX:+PrintGCDetails
    • -XX:+PrintGCTimeStamps
    • -Xloggc:filename
  4. 并行收集器设置
    • -XX:ParallelGCThreads=n:设置并行收集器收集时使用的CPU数。并行收集线程数。
    • -XX:MaxGCPauseMillis=n:设置并行收集最大暂停时间
    • -XX:GCTimeRatio=n:设置垃圾回收时间占程序运行时间的百分比。公式为1/(1+n)
  5. 并发收集器设置
    • -XX:+CMSIncrementalMode:设置为增量模式。适用于单CPU状况。
    • -XX:ParallelGCThreads=n:设置并发收集器年轻代收集方式为并行收集时,使用的CPU数。并行收集线程数。

附:

本系列学习资料主要来自博文http://rednaxelafx.javaeye.com/blog/656951里提到的PPT和《分布式Java应用》里有关JVM的章节,推荐你们继续深刻学习

相关文章
相关标签/搜索