JVM参数设置

1 JDK7和JDK8将字符串常量池存放在了堆中

  字符串常量池string pool中存的是引用值而不是具体的实例对象,具体的实例对象是在堆中存放的,string pool实现为哈希表。

public class TestStringPool {
    //-Xms5m -Xmx5m  -XX:-UseGCOverheadLimit
    //设置最大堆内存和初始堆内存都是5M,
    //-XX:-UseGCOverheadLimit的目的是关闭检查防止抛出GC overhead limit exceeded
    //超过98%的时间用来做GC并且回收了不到2%的堆内存,会抛出GC overhead limit exceeded
    public static void main(String[] args) {
    
        String str = "abc";
        char[] array = {'a', 'b', 'c'};
        String str2 = new String(array);
        //使用intern()将str2字符串内容放入常量池
        str2 = str2.intern();
        //这个比较用来说明字符串字面常量和我们使用intern处理后的字符串是在同一个地方
        System.out.println(str == str2);
        //那好,下面我们就拼命的intern吧
        ArrayList<String> list = new ArrayList<String>();
        for (int i = 0; i < 50000000; i++) {
            System.out.println(i);
            String temp = String.valueOf(i).intern();
            list.add(temp);
        }
    }
}

运行一段时间后会抛出堆内存溢出:

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space

2 元空间MetaSpace溢出

 元空间是用来存放类和类加载器的元信息的,在Jdk8中元空间并不在虚拟机中,而是使用本地内存

public class Test {

    //-XX:MetaspaceSize=20m -XX:MaxMetaspaceSize=20m
    //设置元空间的初始值为20M,最大值为20M
    static int index = 0;
    public static void main(String[] args) {
        while(true) {
        System.out.println(index++);
        Enhancer  enhancer = new Enhancer();
        enhancer.setSuperclass(User.class);
        enhancer.setUseCache(false);
        enhancer.setCallback(new MethodInterceptor() {
            @Override
            public Object intercept(Object obj, Method method, Object[] arg2, MethodProxy proxy) throws Throwable {
                // TODO  Auto-generated method stub
                return proxy.invoke(obj, args);
            }
        });
        
        enhancer.create();
        }
    }
    
    
    static class User{
        
    }
}

运行一段时间后会抛出堆内存溢出:

Exception in thread "main" java.lang.OutOfMemoryError: Metaspace

为了防止应用程序将服务器的内存一直占满,最好要关注下元空间的占用大小

3 堆溢出 

public class Test0004 {
    // 垃圾回收机制基本原则:内存不足的时候回去回收,内存如果足够,暂时不会区回收。尽量减少回收次数和回收的时间
    // -Xms50m -Xmx50m -XX:+PrintGCDetails -XX:+HeapDumpOnOutOfMemoryError
    //-XX:+PrintGCDetails 打印详细日志
    //-XX:+HeapDumpOnOutOfMemoryError 发生OOM时自动生成dump
    public static void main(String[] args) {
        List<Object> listObject = new ArrayList<>();
        for (int i = 0; i < 100; i++) {
            System.out.println("i:" + i);
            Byte[] bytes = new Byte[1 * 1024 * 1024];
            listObject.add(bytes);
        }
        System.out.println("添加成功...");

    }

}

 运行一段时候会抛出异常:

 Exception in thread "main" java.lang.OutOfMemoryError: Java heap space

 4 栈溢出

public class Test005 {

    //-Xss1024k 
    //设置每个栈的大小
    //递归的层数很多会抛出StackOverflowError
    private static int count;

    public static void count() {
        try {
            count++;
            count();
        } catch (Throwable e) {
            System.out.println("最大深度:" + count);
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        count();
    }

}

运行一段时候会抛出异常: 

Exception in thread "main" java.lang.StackOverflowError

 

5 垃圾回收机制:

 垃圾回收指的是不定时去堆内存中清理没有被引用的对象

    堆内存划分:

     垃圾回收机制是怎么判断对象是否存活?

  引用计数法(该方法已经不采用了,当出现循环依赖时,gc没法进行判断)

  引用计数法的基本思路是为每个对象设置一个引用计数器,当gc时,发现该对象被引用,则引用计数器加1,否则引用计数器减1,当引用计数器值为0时,则证明此对象是不可用

     根搜索算法

  根搜索算法的基本思路就是通过一系列名为”GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可用

    可以作为GCRoots的对象包括下面几种

    (1)虚拟机栈(栈帧中局部变量表)中引用的对象。

    (2)方法区中的类静态属性引用的对象。

    (3)方法区中常量引用的对象。

    (4)本地方法栈中JNI(Native方法)引用的对象。

6 垃圾回收算法

     1 标记-清除算法

   分为两个步骤,第一个步骤就是标记,也就是标记处所有需要回收的对象个步骤是清除,标记完成后就进行统一的回收些带有标记的对象缺点是效率低并且标记清除之后会产生大量不连续的内存碎片一般用在老年代

      2 复制算法

  将可用内存按容量划分为大小相等的两块s0区和s1区,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉优点是效率高,可以避免内存碎片问题,缺点是浪费内存空间。一般用在新生代

     3 标记-整理算法

   标记整理算法与标记清除算法很相似,但最显著的区别是:标记清除算法仅对不存活的对象进行处理,剩余存活对象不做任何处理,造成内存碎片;而标记整理算法不仅对不存活对象进行处理清除,还对剩余的存活对象进行重新整理,因此其不会产生内存碎片一般用在老年代

     4 分代算法

  把Java堆分为新生代和老年代,为每个对象设置一个年龄属性,综合使用上面的三种算法

 

7 JVM常见参数配置(JDK8)

-XX:+PrintGCDetails  详细的GC日志

-Xms                          堆初始值

-Xmx                          堆最大可用值

-Xmn                         新生代堆最大可用值(Eden区域和Survivor区域)

-Xss                        每个线程分配的栈大小        

-XX:SurvivorRatio     新生代Eden区域和Survivor区域(From幸存区或To幸存区)的比例,默认为8 ( Eden : From : To = 8 : 1 : 1 )

-XX:NewRatio           新生代与老年代占比 1:2  

-XX:+HeapDumpOnOutOfMemoryError     内存溢出时导出dump文件

-XX:+PrintGCDetails               打印详细gc日志

-XX:MetaspaceSize                元空间初始值

 

-XX:MaxMetaspaceSize          元空间可用最大值

 8 默认收集器

      JDK8默认的垃圾收集器是ParallelGC

   JDK9中默认的垃圾收集器是G1

   新生代 GCMinor GC):指发生在新生代的垃圾收集动作,当新生代满时就会触发Minor GC,这里的新生代满指的是Eden代满,Survivor满不会引发GC 

      老年代 GC(Major GC /Full GC):指发生在老年代的垃圾收集动作,当老代满时会引发Full GC,Full GC将会同时回收新生代、老年代

9 JvisualVm的使用

  为了让本机器上的jvisualvm 工具能够监视远程机器上(linux)的tomcat中线程运行状况,tomcat需要修改其对应配置。修改如下:

    (1) 修改catalina.sh文件

  

CATALINA_OPTS="-Dcom.sun.management.jmxremote.port=1099 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false -Djava.rmi.server.hostname=10.12.145.108"
# ----- Execute The Requested Command -----------------------------------------

  其中红色所指IP是tomcat所属服务器的IP。蓝色所指端口为jmx连接时的端口

    (2) linux防火墙配置

      在(1) 中指定了jmx连接的端口,此时需要查看linux是否开启了该端口,并将该端口设置到防火墙中允许通过

 

 



 

 

原文地址:https://www.cnblogs.com/moris5013/p/11050503.html