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

本文详细探讨了JVM堆内存的结构,包括Eden、Survivor和Tenured区域在新生代与老年代的角色。通过jps和jstat命令,可以观察堆内存状态,并通过生成dump文件来分析内存详情。堆内存的分配和管理对于理解和解决Java应用中的内存溢出问题至关重要。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

        通过前两节,我们了解到了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内存大致就到这里,在以后的垃圾回收并发编程中我们再进行更为详细的介绍。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值