【JVM-02】杂记


编译的时候生成字节码文件,而运行的时候执行字节码文件

1、虚拟机栈

局部变量表和操作数栈的深度在编译期就可以确定下来,并且在后续过程中不会发生改变
在这里插入图片描述

2、栈

  1. 一个Java进程对应一个JVM实例,而一个JVM实例只有一个运行时数据区;当运行多个Java程序时,开启的是多个进程,也就是同时开启了多个JVM
  2. 调整栈的大小并不能保证完全避免StackOverflowError,而只能延迟该错误的出现时间

在这里插入图片描述
虚拟机栈并没有垃圾回收机制,随着出栈就会自动将引用销毁

  1. 当变量在方法内部产生、消亡时,可以说这个方法是线程安全的

3、堆

  1. 设置堆空间大小的参数
    • -Xms 用来设置堆空间(新生代和老年代)的初始内存大小
    • -Xmx 用来设置堆空间(新生代和老年代)的最大内存大小
      其中 -X 是JVM的运行参数, ms 是 memory start

在这里插入图片描述

  1. 可以在Edit Configuration中配置VM options对JVM运行参数进行相关配置
    【建议将Xms和Xmx设置成一个值,防止频繁扩容和释放操作】
    在这里插入图片描述
public static void main(String[] args) {
        //返回Java虚拟机中的堆内存总量
        long initialMemory = Runtime.getRuntime().totalMemory() / 1024 / 1024;
        //返回Java虚拟机试图使用的最大堆内存量
        long maxMemory = Runtime.getRuntime().maxMemory() / 1024 / 1024;

        System.out.println("-Xms : " + initialMemory + "M");
        System.out.println("-Xmx : " + maxMemory + "M");
   }

在这里插入图片描述

  1. 关于设置的是600M而显示的是575M的原因探究及查看JVM设置参数是否生效的两种方法在这里插入图片描述
  • 方法一

运行如下main方法:【注意一定要在运行期间在cmd中查看,所以使用sleep方法使线程睡眠】

public static void main(String[] args) {

        //返回Java虚拟机中的堆内存总量
        long initialMemory = Runtime.getRuntime().totalMemory() / 1024 / 1024;
        //返回Java虚拟机试图使用的最大堆内存量
        long maxMemory = Runtime.getRuntime().maxMemory() / 1024 / 1024;

        System.out.println("-Xms : " + initialMemory + "M");
        System.out.println("-Xmx : " + maxMemory + "M");
        
        try {
            Thread.sleep(1000000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

通过下面的方法可以详细的看到堆内存的分配情况
Java将堆内存在逻辑上分为新生代和老年代
而新生代又分为伊甸园区、幸存者-0、幸存者-1区
600 = S0C+S1C+EC+OC575 =S0C/S1C+EC+OC
在这里插入图片描述
而查看到的是575M的原因为:由于垃圾回收的复制算法,两个幸存者区必须有一个始终为空,而能够存放对象的只有伊甸园区和一个幸存者区

  • 方法二
    在 VM Options中添加-XX:+PrintGCDetails【不需要保持查看期间进程运行,相反,这种查看JVM参数的方法需要的时间点在进程结束后】
    在这里插入图片描述
  1. 有关TLAB
    关于TLAB与加载的相关优先级问题
    下面的内容原文链接点击此处
TLAB空间一般是不会太大,则较大对象肯定无法在此分配,正是TLAB空间的局限性,对象可能很容易将此空间装满。
如一个线程,他有100k的TLAB空间,现在已存入对象占用了80k,现在有个30k的对象肯定无法满足对空间的需求,
所以它存在两种解决方式:
第一种就是放弃当前的TLAB空间,把他还给堆空间,创建新的TLAB。
第二种就是让这个30k的对象在堆空间分配,保留TLAB空间,或许后面会有小于等于20k的对象。

为了解决以上的情况,虚拟机维护了一个refill-waste的值,表示TLAB允许内存浪费的空间。
默认是tlab空间的1/64(在运行时可能会自动调整达到最优化)。
当新的对象请求时空间内存已不足时,会让当前对象所需占用的空间内存和refill-waste比较,
若大于r-w会在堆中分配,保留tlab;若小于r-w会废弃当前Tlab空间新建一个分配对象
  • 对象的大致分配流程:
    • 第一:尝试栈上分配,如不成功第二步。
    • 第二:尝试tlab分配,如不成功第三步。
    • 第三:是否满足老年代分配,如不成功第四步。
    • 第四:Eden分配。

4、方法区

  1. 有关堆、栈、方法区之间的交互关系
    在这里插入图片描述
  2. 修改JVM运行环境(JDK),通过修改以下两处即可
    修改编译环境

修改运行环境

  1. JDK8即前后方法区参数设置对比及注意事项在这里插入图片描述
  2. 字节码文件是一个二进制文件,而要看具体的字节码指令时,需要对其进行反编译/解析(48-局部变量表的认识5:50 有说到反编译是什么
    而反编译的两种途径为:
    • 通过IDEA中的jclasslib插件,下载完成后可以通过下面的方法进行查看在这里插入图片描述
      -在这里插入图片描述
    • 通过在IDEA中的Terminal中输入 javap -v/verbose -p 类.class
      -p是为了看到访问权限较小的属性和方法,如被private修饰的
      还可以通过javap -v/verbose -p 类.class > text.txt获取到反编译后得到的txt文件,可以直接在编辑工具中打开便于查看-在这里插入图片描述在这里插入图片描述
  3. 方法区中是可以看到类加载器具体信息的,而在编译后的字节码文件中是找不到的,因为还没有被加载器加载,在编译的时候,即上面看到的字节码指令,只是编译器生成的静态指令,还没有被类加载器执行
    同样的道理,通过编译获得的字节码文件中包含了常量池,而运行字节码文件时,这个常量池就变成了运行时常量池在这里插入图片描述
    其中,String.intern()可以动态的将对象添加到运行时常量池中

6.关于字符串常量池和静态变量的存放位置
在这里插入图片描述

  • JDK6中,方法区和堆一样,使用JVM分配的内存,HotSpot虚拟机中,方法区的具体实现是永久代,并将静态变量和字符串常量池放在了永久代中
    在这里插入图片描述
  • JDK7中,逐渐实现“去永久代化”,将和静态变量和字符串常量池放进堆中
    在这里插入图片描述
  • JDK8中,摒弃了永久代,转而使用元空间作为方法区的实现模型,且元空间并不占用JVM的分配内存,而是直接使用物理内存,可以看到,静态变量和字符串常量池仍然保存在堆中 在这里插入图片描述
  1. 为什么要把永久代替换为元空间在这里插入图片描述
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

pascalzhli

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值