`
agu
  • 浏览: 37924 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
文章分类
社区版块
存档分类
最新评论

JVM内存段分配

阅读更多

<转>

JVM内存段分配,Java垃圾回收调优,Heap设定

时间:2009-11-23 21:46:46来源:网络 作者:未知 点击:0
1. JVM内存段分配及启动参数:

J2EE服务器的内存组成:
Java堆:我们的程序和对象都在这个堆进行管理
? C堆:当引用到一些Native的对象,如网络访问、OCI方式的数据库连接等都在C堆里进行管理

Java堆的描述:

? Young及Old区域用来
1. JVM内存段分配及启动参数:

J2EE服务器的内存组成:
? Java堆:我们的程序和对象都在这个堆进行管理
? C堆:当引用到一些Native的对象,如网络访问、OCI方式的数据库连接等都在C堆里进行管理

Java堆的描述:

? Young及Old区域用来存放由Java类而生成的内存对象;
? Perm区域用来存放Java类及其他虚拟机自己的静态数据

垃圾回收描述:

垃圾回收分多级,0级为全部(Full)的垃圾回收,会回收OLD段中的垃圾;1级或以上为部分垃圾回收,只会回收Young中的垃圾,内存溢出通常发生于OLD段或Perm段垃圾回收后,仍然无内存空间容纳新的Java对象的情况。

当一个URL被访问时,内存申请过程如下:
A. JVM会试图为相关Java对象在Eden中初始化一块内存区域
B. 当Eden空间足够时,内存申请结束。否则到下一步
C. JVM试图释放在Eden中所有不活跃的对象(这属于1或更高级的垃圾回收);释放后若Eden空间仍然不足以放入新对象,则试图将部分Eden中活跃对象放入Survivor区/OLD区
D. Survivor区被用来作为Eden及OLD的中间交换区域,当OLD区空间足够时,Survivor区的对象会被移到Old区,否则会被保留在Survivor区
E. 当OLD区空间不够时,JVM会在OLD区进行完全的垃圾收集(0级)
F. 完全垃圾收集后,若Survivor及OLD区仍然无法存放从Eden复制过来的部分对象,导致JVM无法在Eden区为新对象创建内存区域,则出现”out of memory错误”

Java堆相关参数:

ms/mx:定义YOUNG+OLD段的总尺寸,ms为JVM启动时YOUNG+OLD的内存大小;mx为最大可占用的YOUNG+OLD内存大小。在用户生产环境上一般将这两个值设为相同,以减少运行期间系统在内存申请上所花的开销。

NewSize/MaxNewSize:定义YOUNG段的尺寸,NewSize为JVM启动时YOUNG的内存大小;MaxNewSize为最大可占用的YOUNG内存大小。在用户生产环境上一般将这两个值设为相同,以减少运行期间系统在内存申请上所花的开销。

PermSize/MaxPermSize:定义Perm段的尺寸,PermSize为JVM启动时Perm的内存大小;MaxPermSize为最大可占用的Perm内存大小。在用户生产环境上一般将这两个值设为相同,以减少运行期间系统在内存申请上所花的开销。

SurvivorRatio:设置Survivor空间和Eden空间的比例

例:
MEM_ARGS="-Xms512m -Xmx512m -XX:NewSize=256m -XX:MaxNewSize=256m -XX:PermSize=128m -XX:MaxPermSize=128m -XX:SurvivorRatio=6"

在上面的例子中:
YOUNG+OLD: 512M
YOUNG: 256M
Perm: 128M
Eden: YOUNG*6/(6+1+1)=192M
Survivor: YOUNG/(6+1+1)=32M

Java堆的总尺寸=YOUNG+OLD+Perm=640M

2. JVM内存分析工具

可以使用HPjmeter来进行分析,需要将JDK的GC日志打开(-verbose:gc),可定期用HPjmeter对GC日志进行分析,Demo工具可提供Java Heap内存变化图,以及垃圾回收图,这样就很容易分析内存溢出时是哪个段产生问题

3. 内存溢出的可能性

1. OLD段溢出

这种内存溢出是最常见的情况之一,产生的原因可能是:
1) 设置的内存参数过小(ms/mx, NewSize/MaxNewSize)
2) 程序问题
? 单个程序持续进行消耗内存的处理,如循环几千次的字符串处理,对字符串处理应建议使用StringBuffer。此时不会报内存溢出错,却会使系统持续垃圾收集,无法处理其它请求,相关问题程序可通过Thread Dump获取(见系统问题诊断一章)

? 单个程序所申请内存过大,有的程序会申请几十乃至几百兆内存,此时JVM也会因无法申请到资源而出现内存溢出,对此首先要找到相关功能,然后交予程序员修改,要找到相关程序,必须在Apache日志中寻找。

? 当Java对象使用完毕后,其所引用的对象却没有销毁,使得JVM认为他还是活跃的对象而不进行回收,这样累计占用了大量内存而无法释放。由于目前市面上还没有对系统影响小的内存分析工具,故此时只能和程序员一起定位。

2. Perm段溢出

通常由于Perm段装载了大量的Servlet类而导致溢出,目前的解决办法:
1) 将PermSize扩大,一般256M能够满足要求
2) 若别无选择,则只能将servlet的路径加到CLASSPATH中,但一般不建议这么处理

3. C Heap溢出

系统对C Heap没有限制,故C Heap发生问题时,Java进程所占内存会持续增长,直到占用所有可用系统内存。

 

Java垃圾回收调优

Java中,通常通讯类型的服务器对GC(Garbage Collection)比较敏感。通常通讯服务器每秒需要处理大量进出的数据包,需要解析,分解成不同的业务逻辑对象并做相关的业务处理,这样会导致大量的临时对象被创建和回收。同时服务器如果需要同时保存用户状态的话,又会产生很多永久的对象,比如用户session。业务越复杂的应用往往用户session包含的引用对象就越多。这样在极端情况下会发生两件事情,long gc pause time 或 out of memory。

一,要解决long pause time首先要了解JVM中heap的结构

 
 

java gc heap

  • Java Heap为什么要分成几个不同的代(generation)? 由于80%-98%的对象的生存周期很短,大部分新对象存放在young generation可以很高效的回收,避免遍历所有对象。
  • young与old中内存分配的算法完全不同。young generation中由于存活的很少,要mark, sweep 然后再 compact 剩余的对象比较耗时,干脆把 live object copy 到另外一个空间更高效。old generation完全相反,里面的 live object 变化较少。因此采用 mark-sweep-compact更合适。

二,Java中四种垃圾回收算法

Java中有四种不同的回收算法,对应的启动参数为
–XX:+UseSerialGC
–XX:+UseParallelGC
–XX:+UseParallelOldGC
–XX:+UseConcMarkSweepGC

1. Serial Collector
大部分平台或者强制 java -client 默认会使用这种。
young generation算法 = serial
old generation算法 = serial (mark-sweep-compact)
这种方法的缺点很明显,stop-the-world, 速度慢。服务器应用不推荐使用。

2. Parallel Collector
在linux x64上默认是这种,其他平台要加 java -server 参数才会默认选用这种。
young = parallel,多个thread同时copy
old = mark-sweep-compact = 1
优点:新生代回收更快。因为系统大部分时间做的gc都是新生代的,这样提高了throughput(cpu用于非gc时间)
缺点:当运行在8G/16G server上old generation live object太多时候pause time过长

3. Parallel Compact Collector (ParallelOld)
young = parallel = 2
old = parallel,分成多个独立的单元,如果单元中live object少则回收,多则跳过
优点:old old generation上性能较 parallel 方式有提高
缺点:大部分server系统old generation内存占用会达到60%-80%, 没有那么多理想的单元live object很少方便迅速回收,同时compact方面开销比起parallel并没明显减少。

4. Concurent Mark-Sweep(CMS) Collector
young generation = parallel collector = 2
old = cms
同时不做 compact 操作。
优点:pause time会降低, pause敏感但CPU有空闲的场景需要建议使用策略4.
缺点:cpu占用过多,cpu密集型服务器不适合。另外碎片太多,每个object的存储都要通过链表连续跳n个地方,空间浪费问题也会增大。

几条经验:
1. java -server
2. 设置Xms=Xmx=3/4物理内存
3. 如果是CPU密集型服务器,使用–XX:+UseParallelOldGC, 否则–XX:+UseConcMarkSweepGC
4. 新生代,Parallel/ParallelOld可设大于Xmx1/4,CMS可设小,小于Xmx1/4
5. 优化程序,特别是每个用户的session中的集合类等。我们的一个模块中session中曾经为每个用户使用了一个ConcurrentHashMap, 里面通常只有几条记录,后来改成数组之后,每台机大概节约了1~2G内存。

不过总的说来,Java的GC算法感觉是业界最成熟的,目前很多其他语言或者框架也都支持GC了,但大多数都是只达到Java Serial gc这种层面,甚至分generation都未考虑。JDK7里面针对CMS又进行了一种改进,会采用一种G1(Garbage-First Garbage Collection)的算法。实际上Garbage-First paper(PDF) 2004年已经出来了,相信到JDK7已经可以用于严格生产环境,有时间也会进一步介绍一下G1。
另外在今年的Sun Tech Days上Joey Shen讲的Improving Java Performance(PDF)也是一个很好的Java GC调优的入门教程。

 

Heap设定与垃圾回收

Java Heap分为3个区,Young,Old和Permanent。Young保存刚实例化的对象。当该区被填满时,GC会将对象移到Old区。Permanent区则负责保存反射对象,本文不讨论该区。
JVM的Heap分配可以使用-X参数设定,

-Xms
初始Heap大小
-Xmx
java heap最大值
-Xmn
young generation的heap大小
JVM有2个GC线程。第一个线程负责回收Heap的Young区。第二个线程在Heap不足时,遍历Heap,将Young 区升级为Older区。Older区的大小等于-Xmx减去-Xmn,不能将-Xms的值设的过大,因为第二个线程被迫运行会降低JVM的性能。
为什么一些程序频繁发生GC?有如下原因:
l         程序内调用了System.gc()或Runtime.gc()。
l         一些中间件软件调用自己的GC方法,此时需要设置参数禁止这些GC。
l         Java的Heap太小,一般默认的Heap值都很小。
l         频繁实例化对象,Release对象。此时尽量保存并重用对象,例如使用StringBuffer()和String()。
如果你发现每次GC后,Heap的剩余空间会是总空间的50%,这表示你的Heap处于健康状态。许多Server端的Java程序每次GC后最好能有65%的剩余空间。
经验之谈:
1ServerJVM最好将-Xms-Xmx设为相同值。为了优化GC,最好让-Xmn值约等于-Xmx1/3[2]
2.一个GUI程序最好是每1020秒间运行一次GC,每次在半秒之内完成[2]
 
注意:
1.增加Heap的大小虽然会降低GC的频率,但也增加了每次GC的时间。并且GC运行时,所有的用户线程将暂停,也就是GC期间,Java应用程序不做任何工作。
2.Heap大小并不决定进程的内存使用量。进程的内存使用量要大于-Xmx定义的值,因为Java为其他任务分配内存,例如每个线程的Stack等。
 
2.Stack的设定
每个线程都有他自己的Stack。

-Xss
每个线程的Stack大小
Stack的大小限制着线程的数量。如果Stack过大就好导致内存溢漏。-Xss参数决定Stack大小,例如-Xss1024K。如果Stack太小,也会导致Stack溢漏。
3.硬件环境
硬件环境也影响GC的效率,例如机器的种类,内存,swap空间,和CPU的数量。
如果你的程序需要频繁创建很多transient对象,会导致JVM频繁GC。这种情况你可以增加机器的内存,来减少Swap空间的使用[2]。
4.4种GC
第一种为单线程GC,也是默认的GC。,该GC适用于单CPU机器。
第二种为Throughput GC,是多线程的GC,适用于多CPU,使用大量线程的程序。第二种GC与第一种GC相似,不同在于GC在收集Young区是多线程的,但在Old区和第一种一样,仍然采用单线程。-XX:+UseParallelGC参数启动该GC。
第三种为Concurrent Low Pause GC,类似于第一种,适用于多CPU,并要求缩短因GC造成程序停滞的时间。这种GC可以在Old区的回收同时,运行应用程序。-XX:+UseConcMarkSweepGC参数启动该GC。
第四种为Incremental Low Pause GC,适用于要求缩短因GC造成程序停滞的时间。这种GC可以在Young区回收的同时,回收一部分Old区对象。-Xincgc参数启动该GC。
分享到:
评论

相关推荐

    JVM内存段分配,Java垃圾回收调优,Heap设定

    JVM内存段分配,Java垃圾回收调优,Heap设定

    JVM内存空间分配笔记

    ### JVM内存空间分配详解 #### 一、JVM内存模型概览 ...综上所述,理解JVM内存分配机制对于Java开发者来说至关重要,这不仅有助于编写高效、稳定的代码,还能在遇到性能瓶颈时快速定位问题并进行优化。

    java中jvm内存分配相关资料总结整理

    本资料总结主要关注JVM内存分配及其运行原理,这对于理解和优化Java应用程序的性能至关重要。 1. **JVM内存结构** JVM内存分为几个关键区域:方法区(Method Area)、堆(Heap)、栈(Stack)、程序计数器(PC ...

    java -jvm 内存分配和jvm调优

    总结,Java JVM内存分配和调优是一项复杂的任务,需要结合实际应用的需求和性能指标来调整。通过理解JVM内存模型,选择合适的垃圾收集器和设置合理的内存参数,可以有效提升Java应用的性能和稳定性。在实践中,不断...

    JVM内存溢出问题解析

    堆的优势是可以动态分配内存大小,生存期也不必事先告诉编译器,但缺点是要在运行时动态分配内存,存取速度较慢。 JVM 如何设置虚拟内存?在 JVM 中,如果 98%的时间是用于 GC 且可用的 Heap size 不足 2%的时候...

    JVM内存分配与垃圾回收详解

    JVM 内存分配与垃圾回收详解 Java 虚拟机(JVM)是 Java 语言的 runtime 环境,它提供了一个平台独立的方式来执行 Java 字节码。JVM 内存分配与垃圾回收是 JVM 中两个非常重要的概念,本文将对 JVM 内存分配与垃圾...

    jvm 内存分析文档

    JVM内存分配策略基于对象的生命周期,新生代采用复制算法,老年代采用标记-压缩或标记-整理算法。内存分配的策略会影响垃圾收集的效率和程序的响应速度。 ### GC(垃圾回收) 垃圾回收是JVM自动管理内存的关键特性...

    idea插件JVM内存工具JProfiler11

    1. **内存分配和泄漏检测**:JProfiler11能详细追踪对象的创建、存活和销毁过程,帮助识别内存泄漏。通过查看对象分配图表,可以发现哪些类或方法在消耗大量内存,从而定位潜在的问题。 2. **垃圾收集分析**:...

    Jvm对象内存分配理解

    Jvm 对象内存分配理解 Jvm 对象内存分配是 Java 虚拟机(Jvm)中的一种机制,用于在堆中分配对象的内存空间。该机制涉及到类加载检查、内存分配、对象初始化等多个步骤。 类加载检查 在 Jvm 对象内存分配中,首先...

    JVM内存模型深度剖析与优化.pdf

    JVM内存优化的目的是尽可能让对象都在新生代里分配和回收,避免频繁对老年代进行垃圾回收。以下是一些JVM内存优化的技巧: 1. 设置适当的堆大小:根据实际项目情况,设置适当的堆大小可以避免频繁的垃圾回收。 2. ...

    03-VIP-JVM内存分配机制与垃圾回收算法1

    4. **JVM内存分配示例分析** 在提供的代码示例中,我们看到当分配给`allocation1`的大对象超过Eden区的容量时,JVM执行了Minor GC。初始时,Eden区被完全使用,而老年代未使用。当我们尝试为`allocation2`分配内存...

    JVM内存参数详解以及配置调优

    在这个资源中,我们将详细讨论 JVM 内存参数的配置和调优,包括 JVM 的结构、内存管理、垃圾回收、堆和非堆内存、内存分配和限制等方面。 JVM 结构 JVM 的结构主要由六个部分组成:JVM API、JVM 内部组件、平台...

    java获得jvm内存大小

    通过动态地调整`-Xms`和`-Xmx`参数,可以在运行时根据实际需求优化内存分配,从而提高程序的性能和稳定性。此外,定期检查和分析`heapSize`、`heapMaxSize`和`heapFreeSize`等指标,可以帮助开发者及时发现潜在的...

    jvm内存分析工具mat

    1. **对象概览**:提供一个整体的内存分配情况,展示哪些类占据了最多的内存空间,以及它们的数量和大小。 2. **支配树**:通过支配树分析,可以找出哪些对象是其他对象生存的原因,从而发现可能的内存泄漏源。 3....

    mat(mac)---jvm内存分析工具

    总之,MAT作为一款强大的JVM内存分析工具,对于优化Java应用的内存使用,提升应用性能,尤其是对于Mac OS X平台的开发者来说,是不可或缺的利器。通过熟练掌握MAT的使用,开发者可以更有效地管理和优化应用程序的...

    JVM内存日志

    - 调整内存分配比例,比如新生代和老年代的比例。 - 使用合适的垃圾收集器,如CMS、G1或ZGC。 - 避免创建大量短生命周期的大对象,减少Full GC的发生。 - 定期分析内存日志,及时发现和解决问题。 总结,通过...

Global site tag (gtag.js) - Google Analytics