美文网首页
6:JVM内存模型深度剖析与优化(JVM可视化工具 与 与 共

6:JVM内存模型深度剖析与优化(JVM可视化工具 与 与 共

作者: _River_ | 来源:发表于2021-04-10 00:28 被阅读0次
    1:JVM可视化工具:(JavaVisualVm)
    注意:了解安装完该工具后 先看后面的  方法区(元空间)(共享)  与 堆 (共享)
    
    jvisualvm(JDK自带调试工具  JavaVisualVm)
    会实时监测JVM中启动的线程  (可以启动一个死循环的线程进行测试)
    
    Visual GC(可视化垃圾回收插件)是需要安装的
    可以在JavaVisualVm的 工具栏->工具->插件—>可用插件 安装Visual GC
    可能会更新失败,可以采用网上教程解决,或者使用离线安装;
    
    如果需要离线安装:
    资源跳转链接:
    https://visualvm.github.io/pluginscenters.html
    1:把下载下来的 Visual GC插件 通过添加插件的方式添加
    2:然后进行安装
    3:然后就可以通过 Visual GC 对某个线程进行 可视化垃圾回收监控
    
    离线安装教程:https://blog.csdn.net/qq_39175358/article/details/104708519
    
    2:方法区(元空间)(共享) 详解运行时数据区(内存模型):
    使用 javap -v Math.class 查看(反编译 加附带信息)
    
    常量池
        1:常量
        2:静态变量 (加了static 关键字的 User)
        3:类信息 (C++ 类的方法名称等等)
    
    方法区里面的user放的是  user在堆里面的地址  
    类比于 main线程 main方法栈帧的局部变量表的math  存放的是math这个对象在堆里面的地址
    
    注意:
    方法区(元空间) 使用的是直接内存(也就是物理机内存)
    方法区(元空间)以前叫做永生代
    方法区(元空间) 也会引发full gc 后续会提及
    
    3:堆 (共享) 详解运行时数据区(内存模型):
    存放:
        如 new出来的Math的  math对象 (非地址  是他的内容)
        如 静态变量 (加了static 关键字的 User)的 user对象(非地址 是他的内容)
    
    注意:类加载器 ClassLoader 的对象也是放在堆里面 实际上就是一个对象 
    
    堆实际上是什么东西:
    默认 老年代:占 三方之二
    默认 年轻代:占 三分之一  其中 Eden占 80%   s0占10%    s1占10%
    
    gc过程(先了解):GCRoot  (引用对象 非垃圾对象)
    什么是GCRoot  如:方法区中的 静态变量 的 user    如:栈(线程)的 局部变量表中的 math 。。。
    
    先粗略了解:
    1:Web应用 new 出来的对象 绝大部分都是先扔年轻代的 Eden
    
    2:如果年轻代的Eden满了   触发步骤3
    3:字节码执行引擎  开启一个垃圾回收线程 进行minor gc   (回收Eden 和 s0 s1)
            1:把GCRoot的非垃圾对象   user math  
                 复制到Survivor区s0  对象的头:年龄+1
            2:把Eden 的全部对象全部回收 (包括user math )
            
    4:如果年轻代的Eden又满了   触发步骤5
    5:字节码执行引擎  开启一个垃圾回收线程 进行minor gc  (回收Eden 和 s0 )
            1:把GCRoot的非垃圾对象 复制到  Survivor区s1  
            2:如果Survivor区s0的对象  user  math 不被回收  复制到  Survivor区s1  同时  user  math 对象的头:年龄+1
                 
            3:把Eden 的全部对象全部回收 (包括user math )
                把Survivor区s0 的全部对象回收 (包括user math )
    
    6:如果年轻代的Eden又满了   触发步骤7
    7:字节码执行引擎  开启一个垃圾回收线程 进行minor gc   (回收Eden 和 s1)
            1:把GCRoot的非垃圾对象 复制到  Survivor区s0 
            2:如果Survivor区s1对象  user  math 不被回收  复制到  Survivor区s0 同时  user  math 对象的头:年龄+1
            
            3:把Eden 的全部对象全部回收 (包括user math )
                把Survivor区s1的全部对象回收 (包括user math )
            
    8:当年龄变成15 (默认)时 会被扔到老年代    (里面静态变量比较多)   
    
    4:GC 可视化简单实战
    public class HeapTest {
    
        public static void main(String[] args) throws InterruptedException {
            ArrayList<Heap> heapArrayList = new ArrayList<>();
            while (true){
              //不断的new 一个对象
                heapArrayList.add(new Heap());
                Thread.sleep(100);
            }
        }
    
    }
    
    public class Heap {
        //一个对象里面起码占 100kb内存
        byte [] bytes = new byte[1024*100];
    
    }
    
    1:注意时间  Compile Time 和 Class Loader Time 
    2:注意 GC Time (垃圾回收的时间)
    3:注意Eden Space 与 Survivor 0  Survivor 1 的关系
    4:注意 Old Gen  缓慢而持续的上升 (当Old Gen满的时候内存溢出 查看代码报错)
    5:注意 MetaSpace 方法区保持不动
    

    图1
        一开始的 GC 回收次数 为0
        Eden在上升
        s0 与 s1 为空
        Enen满后  进入图2
    

    图2:
        此时的 GC 回收次数 为1
        s1 获取 Eden 复制过来的 非垃圾回收对象
        Eden清空后重新上升                 
    
    图3:
        此时的 GC 回收次数 为2
        s0 获取 Eden 复制过来的对象
        s0 获取 s1存在的 非垃圾回收对象
        s1 清空
        Eden清空后重新上升
    

    图2 图3 操作不断重复 
    当:   在Eden到S0或S1   S0->S1 或者 S1->S0 时计转移次数加1  到达计数15时进 Old Gen
    导致Old Gen  缓慢而持续的上升
    
    图四:    
        当Old Gen   第一次需要进行回收的空间满了: 会触发 full gc 回收整个堆 以及 方法区(元空间) 的垃圾回收对象
        但最后full gc都无法回收的时候
        如果Old Gen 满了之后会出现异常:OOM 内存溢出
    
    gc 不管 minor gc(时间短)full gc(时间长) 都会出现 stop the world(STW);
    stop the world(STW):停止掉用户发起的所有线程  (比如下单),避免在收集垃圾的过程中产生新的垃圾
    
    java虚拟机调优就是减少 stop the world(STW)的过程    
    

    5:JVM内存分配设置
    Spring Boot程序的JVM参数设置格式(Tomcat启动直接加在bin目录下catalina.sh文件里):
    
    java 
    ‐Xms2048M ‐Xmx2048M ‐Xmn1024M 
    ‐XX:MetaspaceSize=256M ‐XX:MaxMetaspaceSize=256M 
    ‐Xss512K
    ‐jar microservice‐eureka‐server.jar
    

    方法区(元空间):
         推荐设置 JVM 的元空间大小(不然可能导致大一点的程序启动特别慢):
    
    关于元空间的JVM参数有两个:-XX:MetaspaceSize=N和 -XX:MaxMetaspaceSize=N
    -XX:MaxMetaspaceSize: 设置元空间最大值, 默认是-1, 即不限制, 或者说只受限于本地内存大小。
    -XX:MetaspaceSize: 
        指定元空间触发Fullgc的初始阈值(元空间无固定初始大小), 以字节为单位,默认是21M,
        达到该值就会触发full gc进行类型卸载, 容量分配大小的扩展机制 会对该值进行调整:
        容量分配大小的扩展机制:
            如果释放了大量的空间, 就适当降低该值; 
            如果释放了很少的空间, 那么在不超过-XX:MaxMetaspaceSize(如果设置了的话) 的情况下, 适当提高该值。
    
    这个跟早期jdk版本的-XX:PermSize参数意思不一样,-XX:PermSize代表永久代的初始容量。
    方法区(元空间): 以前叫永久代使用物理机内存
    

    栈(线程):
        ‐Xss512K  是指每给线程 512k
    -Xss设置越小count值越小,说明一个线程栈里能分配的栈帧就越少,但是对JVM整体来说能开启的线程数会更多
    在不调节 -Xss的时候 默认是1m 可以调用23538次 redo方法  产生23538个redo栈帧
    在调节    -Xss为128k的时候     可调用1088次 redo方法 产生1088个redo栈帧
    
    线程结束:栈(线程)内存马上释放
    
    特别注意:我们是没有办法设置系统能开多少个线程的
    
    public class ThreadStackOverflowTest {
        //JVM设置
        // - Xss 128k  -Xss 默认 1M
        static  int count = 0;
        /**
         * 每一次 调用redo的时候
         * main线程 都新增一个 redo 的栈帧
         */
        static  void redo(){
            count ++;
            redo();
        }
    
        public static void main(String[] args) {
            try{
                redo();
            }catch (Throwable e){
                e.printStackTrace();
                System.out.println(count);
            }
        }
    }
    

    项目连接

    请配合项目代码食用效果更佳:
    项目地址:
    https://github.com/hesuijin/hesuijin-study-project
    Git下载地址:
    https://github.com.cnpmjs.org/hesuijin/hesuijin-study-project.git
    
    jvm-module项目模块下  jvmMemoryModel包

    相关文章

      网友评论

          本文标题:6:JVM内存模型深度剖析与优化(JVM可视化工具 与 与 共

          本文链接:https://www.haomeiwen.com/subject/qudgkltx.html