JVM常用参数(七)

 内存监控

-XX:+PrintGC

跟-verbose:gc效果一样 但是不是稳定版后续可能会删除

 -verbose:gc

每次GC(yonggc,fullgc) 打印简单的内存情况

测试代码

  public static void main(String[] args){
            List<Classes> classes=new ArrayList<Classes>();
            int count=0;
            for(int i=0;true;i++){
                classes.add(new Classes());
                if(classes.size()>10000){
                    count++;
                    classes.clear();
                    classes=new ArrayList<Classes>();
                    System.out.println("标记为可回收");
                }
                if(count>20){
                    break;
                }
            }
    }

1253K表示为回收前占用内存 903k表示回收后占用内存 可以发现标记回收后垃圾对象被成功回收, 3584为总内存 最后一个为回收时间 

GC (Allocation Failure 表示为新生代回收

如果我们不标记为可回收会怎么样

因为没有标记为已回收 新生代from to 迭代15次或者满了以后直接放到老年代 循环多次后老年代内存快满了时触发fullGC 因为都没有标记为可回收所以每次回收后 占用内存没有变化 最终导致内存溢出

XX:+DisableExplicitGC

禁止代码调用System.gc() 加了此参数则调用无效

-XX:MaxTenuringThreshold

控制在复制区域存活的年龄,超过年龄才到老年代 默认15,注:但是并不表示到了15才会到老年代,如果eden区回收非垃圾对象在复制区域申请不到足够的内存,则直接托管到老年代
-XX:MaxTenuringThreshold=15

 -XX:+PrintGCDetails 

会自动开启-XX:+PrintGC

 -XX:+PrintGCDetails

打印每次gc的回收情况 程序运行结束后打印堆空间内存信息(包含内存溢出的情况)

 

[PSYoungGen: 512K->400K(1024K)] 表示年轻代占用空间 回收前和回收后

512K->400K(1536K) 表示为java堆的空间总内存的回收签回收后内存

PsYoungGen 为新生代 总内存(total1024k ) 使用(used 462K) 其中:

        eden区  512K

        from区  512K

        to区      521K

 年轻代可用空间为1024可以发现eden+from+to大于1024  新生代的内存为eden+from或者+to 因为年轻代采用复制算法 所以复制区域会有一块儿重复的区域512 不能使用

ParOldGen 为老年代 总空间512K

Metaspace  PermGen永久代废弃 jdk8使用 Metaspace(元空间) 替代 

-XX:+PrintGCTimeStamps

针对GC日志 打印启动到日志打印经历的时间,可以分析每次GC回收的间隔

打印每次gc的间隔的时间戳 full gc为每次对新生代老年代以及整个空间做统一的回收 系统中应该尽量避免

产生fullgc的几种情况   老年代空间不足    持久代(元空间 或者jdk8的元空间)空间不足  手动调用system.gc  可以使用可以DisableExplicitGC来禁止

 -XX:+TraceClassLoading

 打印类加载情况

 

-XX:+PrintClassHistogram 

打印每个类的实例的内存占用情况

通过按ctrl+Break 会打印 用的mac不知道咋么按  哈哈哈

-XX:+PrintHeapAtGC

 每次GC(yonggc,fullgc) 都会打印GC前后的整堆内存占用情况

 

回收前年轻代eden区使用100% 老年代使用0% 回收后 eden区放到from区   老年代使用14%

-Xloggc

配合上面的使用将上面的日志打印到指定文件

-Xloggc:/Users/liqiang/Desktop/logs/log.log   按启动时间生成gc_-%t.log 如:gc_-2022-04-21_21-19-07.log  gc_ewei-com-talk1-2022-04-21_21-11-14.log

 -XX:+HeapDumpOnOutOfMemoryError

 发生内存溢出将堆信息转存起来 以便分析

-XX:HeapDumpPath=/home/ewei/dump/$HOSTNAME-oom-dump.hprof 按启动时间生成 dump_-%t.hprof 

 

 

-XX:HeapDumpPath为转存位置     生成的文件使用JProfiler 打开 分析

-XX:OnOutOfMemoryError

也可以在内存溢出时执行脚本 比如发送邮件给系统管理员脚本
-XX:OnOutOfMemoryError ="sh ~/cleanup.sh" 

-XX:ErrorFile

 只是在针对异常崩溃 如:jdk bug 或者在自己程序有问题调用了调用了native的方法的时候才会有用,什么叫调用了native的方法?比如调用了C/C++写的库文件,比如C/C++里面调用JVM。
针对手动kill 或者linux 自动触发oom kill是是无法触发的
有2种配置
            -XX:ErrorFile=./hs_err_pid<pid>.log  指定当异常崩溃保存的错误日志路径 默认是在jdk目录下,如过有权限的话

-XX:OnError

           -XX:OnError="<cmd args>;<cmd args>”  出现致命ERROR之后运行自定义命令 如:"sh ~/cleanup.sh" 

触发OnError或者ErrorFile的场景比较难模拟

 内存分配参数

-xms注意事项

回收后空闲堆内存小于40%时,JVM就会增大堆直到-Xmx的最大限制;回收后空闲堆内存大于70%时,JVM会减少堆直到-Xms的最小限制多余的内存会归还给操作系统。因此服务器一般设置-Xms、-Xmx 相等以避免在每次GC 后调整堆的大小。

一般我们都会设置一样大,减少归还后申请的性能消耗,如果设置一样大,如果使用内存到达过最大峰值 进程top命令 进程占用内存将不会将来下,由jvm调度

可以通过设置扩容和归还的百分比 
-XX:MinHeapFreeRatio 代表当空闲区域大小下降到该值时,会进行扩容,扩容的上限为 Xmx,

计算公式为:HeapFreeRatio =(CurrentFreeHeapSize/CurrentTotalHeapSize) * 100,值的区间为0到100,默认值为 40。如果HeapFreeRatio < MinHeapFreeRatio,则需要进行堆扩容,扩容的时机应该在每次垃圾回收之后。


-XX:MaxHeapFreeRatio 代表当空闲区域超过该值时,会进行“缩容”,缩容的下限为Xms

空闲堆空间的最大百分比,计算公式为:HeapFreeRatio =(CurrentFreeHeapSize/CurrentTotalHeapSize) * 100,值的区间为0到100,默认值为 70。如果HeapFreeRatio > MaxHeapFreeRatio,则需要进行堆缩容,缩容的时机应该在每次垃圾回收之后。
 

以上2个默认值 可通过jmap查看具体设置值 参考https://www.cnblogs.com/LQBlog/p/10691903.html#autoid-3-0-0

 -Xmx -Xms 堆的最大内存和最小内存(最小内存为初始内存 ,如果满了将不断扩容到最大内存)默认是物理内存的1/64

  -Xmx20m -Xms20m  则固定堆空间为20m(年轻代+老年代)

 -XX:SurvivorRatio  Survivor(from-to)区和eden区的占比 

例如 -XX:SurvivorRatio=5 则是5:1:1

       -XX:SurvivorRatio=8:则是8:1:1

2560*(2/7)  则是from和to的大小 剩下则是eden区的大小

  -XMmn  设置新生代的大小 (绝对值)

 -Xmn2m  则设置新生为2m  

-XX:NewRatio 新生代占老年代的比例

   如果是4 则是1:4   如果是5则为1:5 默认为2

如:

虽然新生代空间为2m但是2m通过计算eden和from to的的空间 但是真实新生代则是 eden+from或者to

 

 

 -XX:PermSize  -XX:PermSize   设置永久代的值和最大值 

因为jdk8之后溢出了永久代 使用元空间代替

MaxMetaspaceSize  元空间大小受制于操作系统内存 

-XX:MetaspaceSize=256m的含义到底是什么呢?其实,这个JVM参数是指Metaspace扩容时触发FullGC的初始化阈值,也是最小的阈值。这里有几个要点需要明确:

  1. 如果没有配置-XX:MetaspaceSize,那么触发FGC的阈值是21807104(约20.8m),可以通过jinfo -flag MetaspaceSize pid得到这个值;
  2. 如果配置了-XX:MetaspaceSize,那么触发FGC的阈值就是配置的值;
  3. Metaspace由于使用不断扩容到-XX:MetaspaceSize参数指定的量,就会发生FGC;且之后每次Metaspace扩容都可能会发生FGC(至于什么时候会,比较复杂,跟几个参数有关);
  4. 如果Old区配置CMS垃圾回收,那么扩容引起的FGC也会使用CMS算法进行回收;
  5. 如果MaxMetaspaceSize设置太小,可能会导致频繁FullGC,甚至OOM

XX:MetaspaceSize太小也会导致频繁扩容 每次扩容都导致【Full GC(Metadata GC Threshold)xxxxx, xxxxx】 建议是不断调整跟max保持一致 

当Metaspace超过max

 

 

 

官方推荐堆空间内存分配

新生代占堆的3/8     幸存代占新生代的1/10

栈空间内内存分配

-Xss 默认256

栈空间是保存变量的地址 所以栈空间的大小决定了方法调用的深度 。比如递归方法会产生大量的变量

设置栈大小为108k  运行 几秒后栈溢出

 

 则报栈溢出

我们如果我们将参数改为10m 则会等很久才会内存溢出

内存分配参数默认值

 

-Xms(minimum memory size for pile and heap)   默认情况下为机器内存的64分之一

-Xmx(maximum memory size for pile and heap)   默认情况下为机器内存的4分之一 等同于-XX:MaxHeapSize

-Xmn(年轻代的大小具体值)默认情况下堆内存的64分之一

-XX:NewRatio(年轻代占老年代的比例)默认为2(1:2) 注:不同垃圾回收器可能不一样,会有一点点差距  比如我测试的CMS和jdk8默认  设置一样的比例和堆大小会有差距,可以通过jstat和jmap查看

-XX:SurvivorRatio(eden区与form to区的比例)默认为8(8:1:1)   设置为4,则表示S0C:S1C:EC=1:1:4 ,设置为1 则是 1:1:! 注:不同垃圾回收器可能不一样,会有一点点差距

jdk永久代大小设置 -XX:PermSize=64M -XX:MaxPermSize=128M 

避免Concurrent Mode Failure

年老代剩余空间>=EDEN+SURVIROR,即:

(Xmx-Xmn)*(1-CMSInitiatingOccupancyFraction/100)>=(Xmn-Xmn/(SurvivorRatior+2))

优化相关

-XX:+UseFastAccessorMethods

get,set 方法转成本地代码(对于jvm来说是冗余代码,jvm将进行优化) 

-XX:LargePageSizeInBytes

涉及MMU原理和TLB原理。我的理解是默认情况会通过遍历虚拟地址页获得具体物理地址,但是遍历查找过程是耗时的,所以上层增加了一层TLB缓存,缓存访问地址对应的虚拟地址页,
则可以减少虚拟地址页的遍历,TLB缓存有限制,所以通过调大虚拟地址页的大小,让TLB可以容纳更多虚拟地址与虚拟地址页的映射,但是又不能调整太大一般6-64M

-XX:+UseFastAccessorMethods

我理解应该是针对成员方法 返回值值字节码是aload_0; getfield #index; areturn或ireturn 可以理解成简单方法比如我们VO里面的get set这种简单方法,将不会进行方法计数,也表示不会被统计为热点方法,经过即时编译器编译进行优化

如:

 

-XX:SoftRefLRUPolicyMSPerMB

软引用的存活时间 参考:https://www.cnblogs.com/LQBlog/p/16075841.html

-Xrs

 减小jvm对操做系统信号(signals)的使用,该参数从1.3.1开始有效;
 从jdk1.3.0开始,jvm容许程序在关闭以前还能够执行一些代码(好比关闭数据库的链接池),即便jvm被忽然终止;
 jvm关闭工具经过监控控制台的相关事件而知足以上的功能;更确切的说,通知在关闭工具执行以前,先注册控制台的控制handler,而后对CTRL_C_EVENT, CTRL_CLOSE_EVENT, CTRL_LOGOFF_EVENT, and CTRL_SHUTDOWN_EVENT这几类事件直接返回true。
 但若是jvm以服务的形式在后台运行(好比servlet引擎),他能接收CTRL_LOGOFF_EVENT事件,但此时并不须要初始化关闭程序;为了不相似冲突的再次出现,从jdk1.3.1开始提供-Xrs参数;当此参数被设置以后,jvm将不接收控制台的控制handler,也就是说他不监控和处理CTRL_C_EVENT, CTRL_CLOSE_EVENT, CTRL_LOGOFF_EVENT, or CTRL_SHUTDOWN_EVENT事件。

-XX:CICompilerCount

设置用于编译的编译器线程数

默认情况下, server JVM的线程数设置为2, clientJVM的线程数设置为1, 如果使用分层编译, 则线程数将缩放为内核数

下面的示例显示如何将线程数设置为2

-XX:CICompilerCount=2

 

-Dsun.rmi.dgc.client.gcInterva

自动调用system.gc() 的时间间隔 毫秒单位如果设置-XX:+DisableExplicitGC 则是空调用

-Dsun.rmi.dgc.client.gcInterval=3600000 
-Dsun.rmi.dgc.server.gcInterval=3600000

-Dfile.encoding

避免中文乱码

-Dfile.encoding=utf-8
从Java 源代码到得到正确的结果,要经过 “Java 源代码-> Java 字节码-> 虚拟机->操作系统->显示设备”的过程。在上述过程中的每一步骤,我们都必须正确地处理中文的编码,才能够使最终显示正确的结果。

"Java 源代码-> Java 字节码":该阶段就是调用javac 进行编译的阶段,javac默认采用系统字符集,比如我们本地机器急就是GBK,如果想用其他的编码,比如UTF-8,可以加上 -encoding UTF-8

"Java 字节码-> 虚拟机->操作系统" :该阶段首先需要JRE或者JDK支持多语言(下载JRE的时候会让你选择英文版还是多语言版),然后就是虚拟机启动的时候使用什么字符集,默认也是采用 当前系统的字符集,如需要修改字符集,加上JAVA的启动参数,-Dfile.encoding=GBK

"操作系统->显示设备":该阶段主要就是需要操作系统支持显示中文就可以,就是安装了中文字体。

如果我们在本地编译(Java 源代码-> Java 字节码 阶段)默认采用了GBK字符集,而"Java 字节码-> 虚拟机->操作系统" 阶段采用Cp1252,因字符集不一致,所以就会乱码。

 

Minor GC

新生代 GC(Minor GC):指发生在新生代的垃圾收集动作,因为 Java 对象大多都具
备朝生夕灭的特性,所以 Minor GC 非常频繁,一般回收速度也比较快。一般感知不到

Full GC

又称为老年代GC或者Major GC 整堆回收 一般会至少伴随一次Minor GC  的速度一般会比 Minor GC 慢 10
倍以上。

其他

即时编译器类型

对于启动性能有要求的短时运行程序,我们会选择C1编译器,对应参数-client,对于长时间运行的对峰值性能有要求的程序,我们会选择C2编译器,对应参数-server。

Java7引入了分层编译,使用-XX:+TieredCompilation参数开启,它综合了C1的启动性能优势和C2的峰值性能优势。

在Java8中默认开启了分层编译,在Java8中,无论是开启还是关闭了分层编译,-cilent和-server参数都是无效的了。当关闭分层编译的情况下,JVM会直接使用C2。

posted @ 2018-06-18 12:54  意犹未尽  阅读(1058)  评论(0编辑  收藏  举报