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.代码执行后的内存空间分配
根据代码,首先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工具了:
当前在查看内存之前,我们首先需要启动程序,在启动程序之前我们把JVM的参数给配置进VM options去:
-Xms30m -Xmx30m -XX:MaxMetaspaceSize=30m -XX:+UseConcMarkSweepGC -XX:-UseCompressedOops
然后启动程序,之后我们需要找到这个java程序的进程号才行,使用命令jps:
如图我这边显示的是 7489,然后复制这个号,然后到HSDB中记录下:
选择第一个选项,然后输入7489即可:
注意:如果使用mac的话,输入进程号会发现报错 attach: task_for_pid(10239) failed: '(os/kern) failure ,这个我查过了1.8就会有这个问题,我们安装好最新的jdk就好,不必在这个问题上花太多时间纠结了
我这边也是安装了最新的jdk,然后就可以了:
根据进程号码打开HSDB工具之后,我们可以看到这个进程下所有的线程,我们运行的main就是要查看的线程,双击可以看到详细的信息,如果我们想要看对象信息,就可以在tools菜单下选择 object histogram,对象信息图
然后我们就可以根据对象的路径来查找,我这里找到之后可以看到这个teacher对象总共产生了两个,双击进去可以看到详细的两个对象信息:
如果想要看到一个对象更加详细的信息就可以再次双击对象:
2.2查看堆信息刚我们看的是对象的信息,接下来我们看一下堆内的信息是怎样的:
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
而我们之前记录的两个对象分为为:
0x0000000111697120 king老师
0x0000000111696eb8 mark老师
那么根据内存地址,还有内存在新生代和老年代的划分就知道,这两个对象目前都在eden区
2.3 查看栈的内存空间从上面的栈内存空间信息我们可以看到右边其实就是两个栈帧,在右边有些图形化的箭头,线条什么的,大体上分为了两个方块,也就是两个栈帧,第一个栈帧我们仔细看下发现有一个 是native的方法,其实就是调用了Thread.sleep 方法,因为在我们的类中,最后一个方法就是使用了线程的睡眠这个native方法。
然后 第二个栈帧是调用了main方法的 *** 作数栈 ,也就是expressoin stack。
2.虚拟机栈的优化技术就以上面两个图作为例子来讲解 ,在之前帖子我们学习到了jvm在执行方法的时候会首先编译成为字节码,然后在栈帧中把局部变量压入 *** 作数栈来进行 *** 作,而对于第一个图中的方法来说,在执行word方法时候传入参数10,这个时候10这个变量是会共享给两个栈帧使用的,这样的话,就节省了空间,提升了效率,可以看图2中,红框比较的就是 *** 作数栈,这个变量被两个栈帧共享使用。
而栈在线程使用完之后就会进行内存的释放,而堆相对于栈则是针对于对象,对象使用完才会释放空间。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)