JVM内存结构和JVM参数调优

近期公司项目运行过程中出现java heap outofmemotyerror异常,导致请求挤压,需要调整JVM虚拟机参数,针对以上问题参考思路,heap堆内存泄露,第一步查看进程堆内存容量及其使用情况,UNIX命令jmap -heap pid(java 应用的进程号),执行命令结果如下图1.1,客户端调用进程服务导致进程新生代堆内存中的eden区内存使用率达到百分之百,故解决方案查看分析进程服务中消耗eden区内存的地方,在查找到的基础上预算jvm堆内存需要调整至多少,通过JVM设置调整系统进程的性能。这只是皮毛接下来文章会介绍JVM虚拟机内部结构和JVM参数优化。

图1.1 查看进程堆内存

JVM虚拟机内部结构

结合JVM内部结构图片分析更加直观易理解,本文章主要讲解,方法区、java堆、栈、本地方法栈、指令计数器、本地方法库和垃圾收集器这些JVM内部组件。

类加载子系统

类加载器加载编译后的.class字节码文件将其读入到JVM内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个java.lang.Class对象,用来封装类在方法区中的数据结构,还要校验加载类的正确性,检查其父类是否被加载,方法执行权限等,如需初始化,初始化过程即为执行类中的静态初始化代码、构造器代码以及静态属性的初始化。

方法区

方法区中保存的都是类的信息,包括类的全限定名,类的父类信息,类的类型具体是类还是接口类的访问修饰符如public、private、abstract、interface等,实现接口类的有序列表等信息。方法区中有个静态区,静态区是专门用来存放静态变量以及静态块的就是常量池。

JAVA堆

JAVA堆是和应用程序关系最密切的内存空间,它是用来保存实例化对象实例的,基本上大多数的对象都保存在java堆上的,因此java堆中内存的回收就至关重要,JVM回收机制和堆的详解稍后详细讲解。故会发生内泄露报OutOfMemotyError异常。

从上图堆内存分析

1.JVM中共享数据空间可以分成三个大区,新生代(Young Generation)、老年代(Old Generation)、永久代(Permanent Generation),其中JVM堆分为新生代和老年代

2.新生代可以划分为三个区,Eden区(存放新生对象),两个幸存区(From Survivor和To Survivor)(存放每次垃圾回收后存活的对象)

3.永久代管理class文件、静态对象、属性等(JVM uses a separate region of memory, called the Permanent Generation (orPermGen for short), to hold internal representations of java classes. PermGen is also used to store more information )

4.JVM垃圾回收机制采用“分代收集”:新生代采用复制算法,老年代采用标记清理算法。

JAVA栈

JAVA栈是每个线程所独有的,每当启动一个新线程的时候,java虚拟机都会为它分配一个java栈。java以栈帧为单位保存线程的运行状态。虚拟机只会对java栈执行两种操作:以栈帧为单位的压栈或者出栈。每个线程包含一个栈区,栈中只保存基础数据类型的对象和自定义对象的引用(不是对象),对象都存放在堆区中 。每个栈中的数据(原始类型和对象引用)都是私有的,其他栈不能访问,其大小也是可以设置为固定值或者动态增加因此会发生OutOfMemoryError错误,如果栈深度大于JVM的站的深度会发生StackOverflowError错误。在HotSopt虚拟机中直接就把本地方法栈和Java栈合二为一。

本地方法栈

本地方法栈与JAVA栈类似,只是本地方法站记录的是调用native的本地方法。

程序计数器

程序计数器也是线程私有的,它记录了每个线程所执行的指令,和接下来要执行的指令的地址,比如if else的跳转。

垃圾收集器

垃圾收集算法

1、标记-清除算法:首先标记出需要回收的对象,在标记完成后统一回收所有被标记的对象 
2、复制算法:将可用内存按容量分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉 
3、标记-整理算法:标记存活的对象都向一端移动,然后直接清理掉端边界以外的内存 
4、分代收集算法:根据各个年代的特点采用最合适的收集算法

垃圾收集器 使用位置使用算法可配合收集器  描述
Serial收集器 新生代复制算法 CMS  单线程、Client模式下默认新生代收集器
ParNew收集器新生代 复制算法  CMSSerial的多线程版本、Server模式下默认收集器、默认线程数=CPU数量
Parallel Scavenge收集器新生代复制算法 多线程、目标关注吞吐量
Serial Old收集器老年代标记-整理 Serial的老年代版本、单线程、Client模式下使用
CMS收集器老年代标记-清除Serial、ParNew并发低停顿、关注最短停顿时间
G1收集器新生代+老年代复制+标记整理 

面向服务端应用、整体基于标记整理,局部(两个Region见)基于复制

 

JVM参数

JVM堆的参数配置

-Xmx3550m:设置JVM最大堆内存为3550M。
-Xms3550m:设置JVM初始堆内存为3550M。此值可以设置与-Xmx相同,以避免每次垃圾回收完成后JVM重新分配内存。
-Xss128k:设置每个线程的栈大小。JDK5.0以后每个线程栈大小为1M,之前每个线程栈大小为256K。应当根据应用的线程所需内存大小进行调整。在相同物理内存下,减小这个值能生成更多的线程。但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在3000~5000左右。需要注意的是:当这个值被设置的较大(例如>2MB)时将会在很大程度上降低系统的性能。
-Xmn2g:设置年轻代大小为2G。在整个堆内存大小确定的情况下,增大年轻代将会减小年老代,反之亦然。此值关系到JVM垃圾回收,对系统性能影响较大,官方推荐配置为整个堆大小的3/8。
-XX:NewSize=1024m:设置年轻代初始值为1024M。
-XX:MaxNewSize=1024m:设置年轻代最大值为1024M。
-XX:PermSize=256m:设置持久代初始值为256M。
-XX:MaxPermSize=256m:设置持久代最大值为256M。
-XX:NewRatio=4:设置年轻代(包括1个Eden和2个Survivor区)与年老代的比值。表示年轻代比年老代为1:4。
-XX:SurvivorRatio=4:设置年轻代中Eden区与Survivor区的比值。表示2个Survivor区(JVM堆内存年轻代中默认有2个大小相等的Survivor区)与1个Eden区的比值为2:4,即1个Survivor区占整个年轻代大小的1/6。
-XX:MaxTenuringThreshold=7:表示一个对象如果在Survivor区(救助空间)移动了7次还没有被垃圾回收就进入年老代。如果设置为0的话,则年轻代对象不经过Survivor区,直接进入年老代,对于需要大量常驻内存的应用,这样做可以提高效率。如果将此值设置为一个较大值,则年轻代对象会在Survivor区进行多次复制,这样可以增加对象在年轻代存活时间,增加对象在年轻代被垃圾回收的概率,减少Full GC的频率,这样做可以在某种程度上提高服务稳定性。

-Xmn,-XX:NewSize/-XX:MaxNewSize,-XX:NewRatio都可以影响年轻代,优先级如下:

高优先级:-XX:NewSize/-XX:MaxNewSize 
中优先级:-Xmn(默认等效 -Xmn=-XX:NewSize=-XX:MaxNewSize=?) 
低优先级:-XX:NewRatio

JVM垃圾回收器配置

-XX:+UseSerialGC:设置串行收集器。
并行收集器(吞吐量优先)
-XX:+UseParallelGC:设置为并行收集器。此配置仅对年轻代有效。即年轻代使用并行收集,而年老代仍使用串行收集。
-XX:ParallelGCThreads=20:配置并行收集器的线程数,即:同时有多少个线程一起进行垃圾回收。此值建议配置与CPU数目相等。
-XX:+UseParallelOldGC:配置年老代垃圾收集方式为并行收集。JDK6.0开始支持对年老代并行收集。
-XX:MaxGCPauseMillis=100:设置每次年轻代垃圾回收的最长时间(单位毫秒)。如果无法满足此时间,JVM会自动调整年轻代大小,以满足此时间。
-XX:+UseAdaptiveSizePolicy:设置此选项后,并行收集器会自动调整年轻代Eden区大小和Survivor区大小的比例,以达成目标系统规定的最低响应时间或者收集频率等指标。此参数建议在使用并行收集器时,一直打开。
并发收集器(响应时间优先)
-XX:+UseConcMarkSweepGC:即CMS收集,设置年老代为并发收集。CMS收集是JDK1.4后期版本开始引入的新GC算法。它的主要适合场景是对响应时间的重要性需求大于对吞吐量的需求,能够承受垃圾回收线程和应用线程共享CPU资源,并且应用中存在比较多的长生命周期对象。CMS收集的目标是尽量减少应用的暂停时间,减少Full GC发生的几率,利用和应用程序线程并发的垃圾回收线程来标记清除年老代内存。
-XX:+UseParNewGC:设置年轻代为并发收集。可与CMS收集同时使用。JDK5.0以上,JVM会根据系统配置自行设置,所以无需再设置此参数。
-XX:CMSFullGCsBeforeCompaction=0:由于并发收集器不对内存空间进行压缩和整理,所以运行一段时间并行收集以后会产生内存碎片,内存使用效率降低。此参数设置运行0次Full GC后对内存空间进行压缩和整理,即每次Full GC后立刻开始压缩和整理内存。
-XX:+UseCMSCompactAtFullCollection:打开内存空间的压缩和整理,在Full GC后执行。可能会影响性能,但可以消除内存碎片。
-XX:+CMSIncrementalMode:设置为增量收集模式。一般适用于单CPU情况。
-XX:CMSInitiatingOccupancyFraction=70:表示年老代内存空间使用到70%时就开始执行CMS收集,以确保年老代有足够的空间接纳来自年轻代的对象,避免Full GC的发生。
其它垃圾回收参数
-XX:+ScavengeBeforeFullGC:年轻代GC优于Full GC执行。
-XX:-DisableExplicitGC:不响应 System.gc() 代码。
-XX:+UseThreadPriorities:启用本地线程优先级API。即使 java.lang.Thread.setPriority() 生效,不启用则无效。
-XX:SoftRefLRUPolicyMSPerMB=0:软引用对象在最后一次被访问后能存活0毫秒(JVM默认为1000毫秒)。
-XX:TargetSurvivorRatio=90:允许90%的Survivor区被占用(JVM默认为50%)。提高对于Survivor区的使用率。

辅助信息配置

-XX:-CITime:打印消耗在JIT编译的时间。
-XX:ErrorFile=./hs_err_pid.log:保存错误日志或数据到指定文件中。
-XX:HeapDumpPath=./java_pid.hprof:指定Dump堆内存时的路径。
-XX:-HeapDumpOnOutOfMemoryError:当首次遭遇内存溢出时Dump出此时的堆内存。
-XX:OnError=";":出现致命ERROR后运行自定义命令。
-XX:OnOutOfMemoryError=";":当首次遭遇内存溢出时执行自定义命令。
-XX:-PrintClassHistogram:按下 Ctrl+Break 后打印堆内存中类实例的柱状信息,同JDK的 jmap -histo 命令。
-XX:-PrintConcurrentLocks:按下 Ctrl+Break 后打印线程栈中并发锁的相关信息,同JDK的 jstack -l 命令。
-XX:-PrintCompilation:当一个方法被编译时打印相关信息。
-XX:-PrintGC:每次GC时打印相关信息。
-XX:-PrintGCDetails:每次GC时打印详细信息。
-XX:-PrintGCTimeStamps:打印每次GC的时间戳。
-XX:-TraceClassLoading:跟踪类的加载信息。
-XX:-TraceClassLoadingPreorder:跟踪被引用到的所有类的加载信息。
-XX:-TraceClassResolution:跟踪常量池。
-XX:-TraceClassUnloading:跟踪类的卸载信息。
 



             

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值