JVM之---Java内存结构(第三篇)

来源:互联网 发布:linux wget命令 编辑:程序博客网 时间:2024/06/06 10:07

        通过前两节,我们了解到了java的大概内存结构,其实JVM的内存分布还是比较复杂的,并且各个java虚拟机不相同,内部结构会有相应的变化,有些时候我们将其理解为概念模型才不会有太多的烦恼,虽然前面的文字描述了那么多关于内存模型的东西,但是可能在你现在安装的JVM中未必就完全按照如此进行分布,需要视具体的版本而定。

为什么还要有这一小节的存在呢?本来想要开始java垃圾回收的文章,但是在整理垃圾回收相关资料的时候,我又决定,需要对堆内存做进一步的划分说明(其实这本身是JVM对垃圾回收的一个规范模型),这样,在理解起来以后的垃圾回收机制就不会显得有那么多的陌生感。

本节中将有如下的一些内容进行说明:

1、堆内存的分布

2、通过jps和jstat命令查看堆内存的情况

3、通过一个dump信息查看堆内存的情况

第一:java堆内存的分布

java的堆内存还可以进一步的细分,现在比较主流的划分为Eden,两个Survivor from,Survivor to ,permanent,tenured,如下图所示,其中Eden,两个Survivor是在新生代中,permanent和tenured在老年代中,什么是新生代和老年代,说的通俗一点就是一个是存放刚创建对象的内存空间,一个是存放早期创建对象的内存空间,如下图所示:

上面的图,其中From Survivor 单词写反了,To Survivor 也写反了,发布之后才注意到)

解释一下上面的图,当创建一个对象的时候,该对象将被存放到Eden区,如果经过了一次垃圾回收,Eden有存活的对象,将会被存放到Survivor区,也就是说Survivor存放的是新一轮GC开始之前存活的对象,如果对象在Survivor中经常使用或者说Survivor空间不足,会将对象放到老年代也就是Tenured区,如果在垃圾回收之后,Survivor没有足够的空间,需要向老年代申请空间的时候,老年代此时也是由于空间不够,此时将会抛出OutOfMemoryError,换言之,堆中经常出现内存溢出的区域是两个Survivor和Tenured区。

第二:jps和jstat命令介绍

我们设计一个程序,使其最终发生堆内存溢出,然后在此期间不断地观察堆内存的变化,其中要使用到两个jdk自带的命令,其中jps是查看当前的jvm进程,而jstat是查看堆内存的变化情况。
      其中jps是一个比较简单的命令,基本上和linux下的ps -ef|grep java没有什么太大的区别,在这里就不作为重点来说了,在后文的jps信任检测工具,我们也会专门进行jdk自带命令的详解;
      这里需要重点所一下的是jstat命令,格式为:jstat -<option> [-t] [-h<lines>] <vmid> [<interval> [<count>]]
     其中option的选项有如下许多:
1、-class 监视类装载的情况,数量,内存浪费时间等;
2、-gc  监视堆的状况
3、-gccapacity 和gc类似
4、-gcutil  和-gc类似但是输出已使用的空间
5、-gccause 和-gcutil类似
6、-gcnew 检测新生代
7、-gcnewcapacity 和-gcnew类似
8、-gcold 检测老年代
9、-gcoldcapacity 和-gcold类似
10、-gcpermcapacity 输出永久代
11、-compiler 输出JIT编译器过的方法耗时等信息
12、-printcompliation 输出已输入的JIT编译的方法

第三:生成dump并且简单说明

      好了,命令暂时说道这里,在这快,我们做一个堆内存溢出的实验,并且进行jstat分析,然后看一下其中堆内存空间的变化情况。看看如下的java代码:
import java.util.List;import java.util.ArrayList;/** *@desc 进行堆内存溢出的测试 *@author wangwenjun(QQ:532500648) *@since 1.0.0 * */public class HeapMemoryTest{static class Test{}public static void main(String[] args){List list = new ArrayList();/*休眠时间长一点,是为了执行jps和jstat命令先*/try{Thread.sleep(10000);}catch(Exception e){};while(true){list.add(new Test());try{Thread.sleep(2);/*休眠,能更好的查看到内存的变化*/}catch(Exception e){};}}}

编译好之后,使用如下命令执行:
java -verbose:gc -Xms10M -Xmx10M -XX:+PrintGCDetails HeapMemoryTest
我们打开两个窗口,第一个窗口用来运行class文件,另外一个用来执行jstat命令:jstat -gc <pid> 1000 1000(查看pid的内存信息),分别进行了两次采样,其中第一次但Eden区快要满的时候的截图如下所示

      第二次Eden区快要满的时候的截图,我们可以看到上一次中内存分布发生了如下的变化,s1区被填满,并且老年代中的空间也多了445.6

      当发生gc的时候,我们暴力的然jvm输出了dump信息,如下所示,分别是两次GC的截图(最后一个是我进行关闭时候的截图)



     java内存大致就到这里,在以后的垃圾回收并发编程中我们再进行更为详细的介绍。


原创粉丝点击