JVM(二)JVM内存结构研究

 一、JVM内存结构概览

  由上图可以看到,一个程序在运行时,JVM数据区主要有这几块组成部分,且每一块组成部分都可以通过设置对应的虚拟机参数来进行调优。

  Spring Boot应用包运行时可以通过以下格式的指令进行启动(Tomcat启动直接加在bin目录下catalina.sh文件里):

java ‐Xms2048M ‐Xmx2048M ‐Xmn1024M ‐Xss512K ‐XX:MetaspaceSize=256M ‐XX:MaxMetaspaceSize=256M ‐jar 应用程序包名.jar

  PS:-Xss设置越小时,一个线程栈里能分配的栈帧就越少【循环计算的次数越少】,但是对JVM整体来说能开启的线程数会更多。

二、举一个栗子

三、JVM内部做了啥呢?~

  操作数栈:操作数在程序运行期间做操作时,临时存放的一块内存空间。

  动态链接:Java Class文件中有很多符号引用,一部分在类加载的时候转化为直接引用,另一部分在运行期间转化为直接引用,这部分被称为动态链接。【符号引用转直接引用的过程

  方法出口:当一个方法执行的时候,只有两种可以退出方法的方法:

  • 正常完成出口:JVM碰到任意一个方法返回的字节码指令。
  • 异常完成出口:在执行方法中抛出异常并且未对异常进行处理。

  PS:方法退出的时候相当于把栈帧出栈。

  PS:栈内若有对象类型的局部变量,则局部变量表内部存放的是该对象在堆上的内存地址,而非对象本身。

  PS:元空间使用的是我们内存条的物理内存。(jdk1.8以后才叫元空间,jdk1.8之前叫永久代)

元空间

  关于元空间的JVM参数有两个:-XX:MetaspaceSize=N和 -XX:MaxMetaspaceSize=N

  • -XX:MaxMetaspaceSize设置元空间最大值, 默认是-1, 即不限制, 或者说只受限于本地内存大小。
  • -XX:MetaspaceSize指定元空间触发Fullgc的初始阈值(元空间无固定初始大小), 以字节为单位,默认是21M,达到该值就会触发full gc进行类型卸载, 同时收集器会对该值进行调整:
    • 如果释放了大量的空间,就适当降低该值
    • 如果释放了很少的空间, 那么在不超过-XX:MaxMetaspaceSize(如果设置了的话)的情况下, 适当提高该值

  PS:1.7jdk版本的-XX:PermSize参数代表永久代的初始容量。 由于调整元空间的大小需要进行Full GC,这是非常昂贵的操作,如果应用在启动的时候发生大量Full GC,通常都是由于永久代(jdk1.7及以前)或元空间(jdk1.8+)发生了大小调整【可能会导致项目启动时间过长】,基于这种情况,一般建议在JVM参数中将MetaspaceSize和MaxMetaspaceSize设置成一样的值,并设置得比初始值要大, 对于8G物理内存的机器来说,一般将这两个值都设置为256M。

四、垃圾回收(GC)机制图解【存活对象的活动路径】

  JVM内部有一个自己的垃圾回收机制【针对堆中的对象进行回收】,把没用的对象进行释放换取内存,我们通过一个流程图来简单的看一下JVM在堆中是怎么操作对象并进行GC操作的:

STW

  在GC的时候有一个STW【stop the world】的概念,就是在GC进行时,JVM会暂时停止所有正在运行的线程,来进行垃圾对象的查找,此时用户访问程序时就会有感觉网络卡顿加载不出来的用户体验。

  PS:之所以这样设计,是因为每一次Full GC的消耗很大,如果不先把所有线程停止,此时可能会产生这种情况:

  • 当GC查找到对象A的时候,发现对象A还在被线程使用,此时JVM不会回收对象A及对象A引用的一连串对象。
  • 当GC开始检查下一个对象的时候,对象A的线程已经结束,此时对象A和其引用的对象已经变成垃圾对象,GC却没有回收掉,就会白白浪费很多时间,GC的效果也极其不好。

五、小问答

Q:类对象和类信息的区别

  类对象一般存在于堆中,主要是通过类实例化之后产生的。类信息则存放在方法区(元空间)里,主要内容是类的一些常量池,字节码文件等内容。

posted @ 2021-03-31 21:42  有梦想的肥宅  阅读(223)  评论(0编辑  收藏  举报