JVM学习二·实例代码运行JVM内存处理全流程

image

image


public class sample {

    public final static String MAN_TYPE = "man";
    public static String WOMAN_TYPE = "woman";

    public static void main(String[] args) throws Exception {
        Teacher t1 = new Teacher();
        t1.setName("Mark");
        t1.setSexType(MAN_TYPE);
        t1.setAge(36);
        Teacher t2 = new Teacher();
        t2.setName("King");
        t2.setSexType(MAN_TYPE);
        t2.setAge(18);
        Thread.sleep(Integer.MAX_VALUE);
    }

    static class Teacher{
        String name;
        String sexType;
        Integer age;

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public String getSexType() {
            return sexType;
        }

        public void setSexType(String sexType) {
            this.sexType = sexType;
        }

        public Integer getAge() {
            return age;
        }

        public void setAge(Integer age) {
            this.age = age;
        }
    }

}

1.代码执行后的内存空间分配

image

根据代码,首先jvm启动时候就会先生成方法区与堆内存,然后方法启动之前会先启动线程,这个时候就会生产栈内存空间,执行方法的时候就会以压栈的方法入栈;

然后静态常量与静态变量进入方法区,main方法中的两个new对象在堆内存中生成,栈空间存放引用。在此我向大家推荐一个架构学习交流圈。交流学习指导伪鑫:1253431195(里面有大量的面试题及答案)里面会分享一些资深架构师录制的视频录像:有Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化、分布式架构等这些成为架构师必备的知识体系。还能领取免费的学习资源,目前受益良多

在堆中生成的对象又会在新生代老年代之间进行轮转。

2.代码为例来详细观察下内存的变化

2.1使用工具HSDB

观察内存空间我们可以使用JHSDB工具,如果是java8的话可以这么操作:

到jdk的安装目录下找到sa-jdi.jar ,然后复制到jre目录的lib下面

备注:如果是Mac,可以到/Library/Java/JavaVirtualMachines找到jdk的安装目录,jre的目录位置可以这么查找:/usr/libexec/java_home -V 之后最后几行中可以找到路径

然后再到jre的lib目录下面执行命令:

注意:sa-jdi.jar 根据自己实际jar包所在位置执行 ,不要直接抄下面命令*

java -cp /Library/Java/JavaVirtualMachines/jdk1.7.0_80.jdk/Contents/Home/lib/sa-jdi.jar sun.jvm.hotspot.HSDB

然后就可以启动HSDB工具了:

image

当前在查看内存之前,我们首先需要启动程序,在启动程序之前我们把JVM的参数给配置进VM options去:

-Xms30m -Xmx30m -XX:MaxMetaspaceSize=30m -XX:+UseConcMarkSweepGC -XX:-UseCompressedOops

image

然后启动程序,之后我们需要找到这个java程序的进程号才行,使用命令jps:

image

如图我这边显示的是 7489,然后复制这个号,然后到HSDB中记录下:

image

选择第一个选项,然后输入7489即可:

注意:如果使用mac的话,输入进程号会发现报错 attach: task_for_pid(10239) failed: '(os/kern) failure ,这个我查过了1.8就会有这个问题,我们安装好最新的jdk就好,不必在这个问题上花太多时间纠结了

我这边也是安装了最新的jdk,然后就可以了:

image

根据进程号码打开HSDB工具之后,我们可以看到这个进程下所有的线程,我们运行的main就是要查看的线程,双击可以看到详细的信息,如果我们想要看对象信息,就可以在tools菜单下选择 object histogram,对象信息图

image

然后我们就可以根据对象的路径来查找,我这里找到之后可以看到这个teacher对象总共产生了两个,双击进去可以看到详细的两个对象信息:

image

如果想要看到一个对象更加详细的信息就可以再次双击对象:

image

2.2查看堆信息

刚我们看的是对象的信息,接下来我们看一下堆内的信息是怎样的:

image

Heap Parameters:
Gen 0:   eden [0x0000000111200000,0x00000001116ba200,0x0000000111a00000) space capacity = 8388608, 59.088134765625 used
  from [0x0000000111a00000,0x0000000111a00000,0x0000000111b00000) space capacity = 1048576, 0.0 used
  to   [0x0000000111b00000,0x0000000111b00000,0x0000000111c00000) space capacity = 1048576, 0.0 usedInvocations: 0

Gen 1: concurrent mark-sweep generation
free-list-space[ 0x0000000111c00000 , 0x0000000113000000 ) space capacity = 20971520 used(0%)= 0 free= 20971520
Invocations: 0

上面的信息我解释一下,这个是堆的信息,总共分为四个区域:
其中eden,from和to都从属于新生代,最后一个gen1是老年代

  • eden区 开始地址:0x0000000111200000 结束地址为:0x0000000111a00000
  • from区 开始地址:0x0000000111a00000 结束地址:0x0000000111b00000
  • to区 开始地址:0x0000000111b00000 结束地址:0x0000000111c00000
  • gen1(老年代) 开始地址为 :0x0000000111c00000 结束地址: 0x0000000111c00000

image

而我们之前记录的两个对象分为为:

0x0000000111697120  king老师 
0x0000000111696eb8  mark老师

那么根据内存地址,还有内存在新生代和老年代的划分就知道,这两个对象目前都在eden区

2.3 查看栈的内存空间

image

image

从上面的栈内存空间信息我们可以看到右边其实就是两个栈帧,在右边有些图形化的箭头,线条什么的,大体上分为了两个方块,也就是两个栈帧,第一个栈帧我们仔细看下发现有一个 是native的方法,其实就是调用了Thread.sleep 方法,因为在我们的类中,最后一个方法就是使用了线程的睡眠这个native方法。

然后 第二个栈帧是调用了main方法的操作数栈 ,也就是expressoin stack。

2.虚拟机栈的优化技术

image

image

就以上面两个图作为例子来讲解 ,在之前帖子我们学习到了jvm在执行方法的时候会首先编译成为字节码,然后在栈帧中把局部变量压入操作数栈来进行操作,而对于第一个图中的方法来说,在执行word方法时候传入参数10,这个时候10这个变量是会共享给两个栈帧使用的,这样的话,就节省了空间,提升了效率,可以看图2中,红框比较的就是操作数栈,这个变量被两个栈帧共享使用。

而栈在线程使用完之后就会进行内存的释放,而堆相对于栈则是针对于对象,对象使用完才会释放空间。

猜你喜欢

转载自blog.csdn.net/muli522/article/details/124737946
今日推荐