[转]JVM调优原理与常见异常处理方案

在jvm调优之前,我们必须先了解jvm的内存模型与GC回收机制,这些在我前面的文章里面有介绍!接下来我们通过一个案例来调整jvm性能。

一 测试案例:

1.1 编写demo

import java.text.DecimalFormat;
/**
    -XX:+PrintGC         打印GC日志
    -XX:+PrintGCDetails  打印详细的GC日志
    file.encoding         文件编码
    -XX:MaxTenuringThreshold 对象年龄,默认15次之后较大几率放进入老年代,为0则不进入s0 s1区,直接进老年代
    
    -XX:+UseSerialGC       串行收集器
    -XX:+UseParallelOldGC  并行收集器
    -XX:+UseParallelGC     合并回收
    -XX:ParallelGCThreads  并行收集器线程数量
    
    -Xms               堆初始值
    -Xmx               堆最大可用值
    -XX:SurvivorRatio  新生代中eden空间和from(s0),to(s1)空间的比例
    -XX:NewRatio       新生代与老年代的比例
    -Xss  栈的大小(栈的深度)
    
    -XX:MetaspaceSize     元空间初始值
    -XX:MaxMetaspaceSize  元空间最大值
    
    -XX:+HeapDumpOnOutOfMemoryError:内存溢出生成快照文件
 */
public class JVMDemo {
    public static void main(String[] args) throws InterruptedException {
        // 最大内存
        long maxMemory = Runtime.getRuntime().maxMemory();
        System.out.println("最大堆内存为: "+format(maxMemory)+"MB");
        // 已经使用内存
        long totalMemory = Runtime.getRuntime().totalMemory();
        System.out.println("已使用堆内存: "+format(totalMemory)+"MB");
        // 当前剩余内存
        long freeMemory = Runtime.getRuntime().freeMemory();
        System.out.println("剩余堆内存为: "+format(freeMemory)+"MB");
                
        byte[] b1 = new byte[4 * 1024 * 1024];
        System.out.println("-----分配了4m堆内存-----");
        
        // 当前剩余内存
        long freeMemory2 = Runtime.getRuntime().freeMemory();
        System.out.println("剩余堆内存为: "+format(freeMemory2)+"MB");
    }
    /**
     * 将堆内存单位格式化成 MB
     */
    static private String format(long maxMemory) {
        float num = (float) maxMemory / (1024 * 1024);
        DecimalFormat df = new DecimalFormat("0.00");// 格式化小数
        String s = df.format(num);// 返回的是String类型
        return s;
    }
}

1.2 配置参数,打印jvm信息: 右键 --> Run As --> Run Configurations… --> Arguments --> VM arguments 输入配置信息 -XX:+PrintGCDetails -XX:+UseSerialGC
引用图片1.3 运行java代码, 查看jvm信息

// 这些信息与电脑配置参数有关,我们的具体数据可能不一样,但是内存模型数据比例是一样的
最大堆内存为: 1890.81MB  // 大约1900MB
已使用堆内存: 119.88MB   // 已用120MB
剩余堆内存为: 117.89MB   // 剩余117MB
-----分配了4m堆内存-----
剩余堆内存为: 113.89MB   // 用了4mb后还剩113MB
Heap
 def new generation   total 38080K, used 6805K [0x0000000085c00000, 0x0000000088550000, 0x00000000ae800000)
 // 可以看出新生代中默认 eden区 from区 to区比例为 33856:4224:4224 即 8:1:1
  eden space 33856K,  20% used [0x0000000085c00000, 0x00000000862a57a8, 0x0000000087d10000)
  from space 4224K,   0% used [0x0000000087d10000, 0x0000000087d10000, 0x0000000088130000)
  to   space 4224K,   0% used [0x0000000088130000, 0x0000000088130000, 0x0000000088550000)
 // 老年代和新生代默认比例为 84672:(33856+4224+4224)  即2:1
 tenured generation   total 84672K, used 0K [0x00000000ae800000, 0x00000000b3ab0000, 0x0000000100000000)
   the space 84672K,   0% used [0x00000000ae800000, 0x00000000ae800000, 0x00000000ae800200, 0x00000000b3ab0000)
 // 元空间
 Metaspace       used 3652K, capacity 4600K, committed 4864K, reserved 1056768K
  class space    used 410K, capacity 428K, committed 512K, reserved 1048576K

调优总结:

1.将初始的堆大小与最大堆大小相等,来垃圾回收次数从而提高效率。
2.根据自身服务器性能,将堆内存最大化,以此提高吞吐量。
3.将新生代或老年代的比例设置为1/2 或者 1/3,让GC尽量去新生代去回收。
4.合理的使用并行收集器。

在windows中设置JVM参数

修改 tomcat/bin/catalina.bat 文件
set JAVA_OPTS=-Dfile.encoding=UTF-8 -server -XX:+PrintGCDetails -Xms900m -Xmx900m -XX:SurvivorRatio=8 -XX:NewRatio=2 -XX:+HeapDumpOnOutOfMemoryError -XX:+UseParallelGC -XX:+UseParallelOldGC -XX:ParallelGCThreads=8 -XX:MaxMetaspaceSize=128m 

JVM参数

在linux中设置JVM参数

修改 tomcat/bin/catalina.sh 文件
JAVA_OPTS="-Dfile.encoding=UTF-8 -server 
           -XX:+PrintGCDetails 
           -Xms1000m -Xmx1000m 
           -XX:SurvivorRatio=8 -XX:NewRatio=2 
           -XX:MaxMetaspaceSize=200m
           -XX:+HeapDumpOnOutOfMemoryError
           -XX:+UseParallelGC -XX:+UseParallelOldGC 
           -XX:ParallelGCThreads=8"

LinuxJVM参数

什么是内存泄漏及如何避免

内存泄漏即:对象可达但不可用,程序不需要用某个对象,但另一个正在使用的对象却持有它的引用,导致无法回收停留在堆内中。

防止内存泄露:

1.尽早释放无用对象的引用, 将不需要使用的对象设置为null,暗示垃圾收集器来收集该对象,防止发生内存泄漏。

2.程序进行字符串处理时,尽量避免使用String,而应该使用StringBuffer。

3.尽量少用静态变量

4.尽量运用对象池技术以提高系统性能

Jvm常见异常处理方案

内存泄漏: java.lang.OutOfMemory Error:Java heap space

解决思路: 1. 先查看是不是内存泄漏,如果是通过GC Root的路径来排查

2. 查看堆内存是否有对象没释放。

3. 加大物理内存 –Xms, -Xmx,最好-Xms = -Xmx,减少内存扩展的开销。

             控制参数:
               -Xms (starting 堆的起始大小)   
               -Xmx (max 堆的最大大小)      
               -Xmn (new 堆的新生代大小)

内存溢出: java.OutOfMemory Error:PermGen space
解决思路:

增加参数:
-XX:PrintGCDetails,
-XX:+PrintGCTimeStamps
-XX:+PrintGCDateStamps 

线程请求的栈深度大于虚拟机所允许的最大深度:java.lang.StackOverflow Error

解决思路: 可以将栈的深度,理解为数组的长度。

控制参数:  
                 1. 加大-Xss(每个线程的堆栈大小)参数。  

           2. 更换64位虚拟机。

           3. 减少线程。        

           4. 减少最大堆(Xmx)。
java.lang.OutOfMemory Error,有allocate、Native字样

解决思路: 加大本地内存-MaxDirectMemorySize如不指定则与-Xmx一致。

以centos6.8 ,100G的内存 jdk1.7为例,参考配置如下:

JAVA_OPTS="-Xms8g -Xmx8g

-XX:ParallelGCThreads=8

-XX:PermSize=2g

-XX:MaxPermSize=4g

-Xss512k -Xmn6g

-XX:-DisableExplicitGC

-XX:+UseCompressedOops

-XX:+UseConcMarkSweepGC

-XX:+CMSParallelRemarkEnabled"

CATALINA_OPTS="-Xms8g -Xmx8g

-XX:ParallelGCThreads=8

-XX:PermSize=2g

-XX:MaxPermSize=4g

-Xss512k -Xmn6g

-XX:-DisableExplicitGC

-XX:+UseCompressedOops

-XX:+UseConcMarkSweepGC

-XX:+CMSParallelRemarkEnabled"

转载自吴磊的博客
https://www.cnblogs.com/wlwl/p/9941196.html

猜你喜欢

转载自blog.csdn.net/huoliangwu/article/details/84613967
今日推荐