JMM与JVM

0 篇文章 0 订阅

JMM与JVM

Java是运行在虚拟机上面的,这也是为什么Java能跨平台运行的原因,作为Java程序的底层,了解JVM内存结构就显得很重要了。有一个很常见的误解,JVM内存结构与Java内存模型到底指的是不是同一个东西。其实他们不是同一个东西来的。(以下基于JDK1.8)

  • JVM内存结构指的是一个规范,规范里面规定了JVM内存结构要有类装载子系统、方法区,Java堆,Java栈、线程计数器、本地方法栈、执行引擎。GC垃圾回收器这些东西。而各个不同JDK则是实现了这些规范,他们的Java内存模型,会有一些不一样。
  • 用一张图表示JVM内存结构(图1)
    JVM内存模型
    JVM内存模型
  • 了解基本概念之后可以开始看看主流的Java内存模型是长什么样的用一张图来表示(这张图画了好久 -- 图2)
    image
    image
  • 口说无凭 通过一个小例子看看栈是怎么执行的
 1/**
2 * 一段简单的代码 一看便知道执行的结果是8
3 * 那在虚拟机底层这个 8 是怎么计算出来的呢
4 */

5public class MyTest0 {
6    public static void main(String[] args{
7        sum();
8    }
9    static void sum({
10        int a = 5;
11        int b = 3;
12        System.out.println(a + b);
13    }
14}

在当前Java文件的目录下输入 以下指令编译成MyTest0.class文件

javac MyTest0.java

这个时候会产生一个MyTest0.class的字节码文件,我们是看不懂的 截取一部分大概长这样子(不同的编码环境下 打开会不一样)

1漱壕   4 
2  
3        
4     <init> ()V Code LineNumberTable main ([Ljava/lang/String;)V sum 
5SourceFile 

可以使用以下指令对class文件进行反编译,在控制台输出反编译后的代码

javap -c MyTest0

为了方便编辑 在window环境下使用 以下指令 可以将编译后的代码之间输出到MyTest0.txt

javap -c MyTest0 > MyTest0.txt

然后用编辑器打开 MyTest0.txt 长这个样子的
好像还是看不懂hh,这个时候告诉自己不要慌,这些东西需要结合Jvm指令集与上面的那张图2 结合起来解析
PS ( /--/ 开头的注释是我自己加的说明)

 1Compiled from "MyTest0.java"
2public class com.dms.MyTest0 {
3  public com.dms.MyTest0();
4    Code:
5       0: aload_0 
6       /--/  调用超类构造方法,实例初始化方法,私有方法 (看这里就能明白为什么,为什么类里面没有构造方法却能new对对象,编译器会自动调用超类构造方法)
7       1: invokespecial #1  // Method java/lang/Object."<init>":()V 
8       4return
9
10  public static void main(java.lang.String[]);
11    Code:
12       0: invokestatic  #2  // Method sum:()V  /--/ 调用静态方法 sum()
13       3return
14
15  static void sum();
16    Code:
17       0: iconst_5  /--/ 将int5推送至sum方法栈顶
18       1: istore_0  /--/ 将栈顶int型数值存入第一个本地变量(也就是存到sum局部变量表)
19       2: iconst_3  /--/ 将int3推送至sum方法栈顶
20       3: istore_1  /--/ 将栈顶int型数值存入第二个本地变量(也就是存到sum局部变量表)
21       /--/ 获取指定类的静态域,并将其值压入栈顶
22       4: getstatic     #3  // Field java/lang/System.out:Ljava/io/PrintStream;
23       7: iload_0   /--/ 将第一个int型本地变量推送至栈顶(也就是把局部变量里的5 推至栈顶)
24       8: iload_1   /--/ 将第二个int型本地变量推送至栈顶(也就是把局部变量里的3 推至栈顶)
25       9: iadd      /--/ 将栈顶两int型数值相加并将结果压入栈顶 也就是8
26      10: invokevirtual #4  // Method java/io/PrintStream.println:(I)V /--/  调用实例方法 也就是调用打印方法把8打印出来
27      13return    /--/ 返回
28}

基础流程就是图2与以上 /--/ 注释所写的那样子了 自己跟着流程操作一遍可能会更好理解一些,更多的JVM指令集可以参考以下链接,或自行网上搜索

https://www.cnblogs.com/dreamroute/p/5089513.html

  • 图二说过当新new出来的对象很大时该对象会直接进入老年代,直接跑段代码看看是不是真的是这样子的。
 1public class MyTest {
2    /**
3     * 每个对象有一个1M的属性 使它一创建出来就是一个大对象
4     */

5    byte[] bytes = new byte[1024 * 1000];
6
7    public static void main(String[] args) throws InterruptedException {
8        // 程序刚启动时JVM会有一些初始化的处理,为避免影响实验结果,先睡眠5秒
9        Thread.sleep(5000);
10        // 为避免垃圾回收机制自动回收没有引用的对象,这里用一个数组先装着,保证对象存活
11        List list = new ArrayList<>();
12        // 循环添加1000个大对象
13        for (int i = 0; i < 1000; i++) {
14            System.out.println(i + "开始添加对象===");
15            Thread.sleep(100);
16            list.add(new MyTest());
17        }
18
19        System.out.println("开始清理垃圾");
20        // 将引用置为空 然后通知JVM回收空对象
21        list = null;
22        System.gc();
23        // 睡眠一会 防止执行垃圾回收前虚拟机就退出了 
24        Thread.sleep(10000);
25    }
26}

以上代码的执行结果,心里预期应该是老年代的空间在1000个对象添加完成前一直会保持增长,在添加完一千个对象之后执行垃圾回收,空间又会断崖式的下跌。口说无凭,我们使用个工具观察一下老年代的内存情况。这里使用的工具是JDK带的jconsole

window环境下使用小黑窗输入jconsole 即可调出来 选择本地进程 选择自己的程序

image
image

然后选择内存 内存池"PS Old Gen" 观察老年代的内存变化 可以看到内存曲线变化是符合心理预期的

image
image
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值