JVM之执行时数据区简析及深入浅出栈、堆

首先先看看JVM的结构图:
在这里插入图片描述
关于类加载器的概述可以看这篇博客:JVM之类加载器

当然在讲解堆和栈之前,我们先了解运行时数据区的其他区域是干什么用的:

本地方法栈

本地方法栈是为本地方法服务的,虚拟机规范中对本地方法栈中方法使用的语言、使用方式与数据结构并没有强制规定,因此具体的虚拟机可以自由实现它。
这里举个例子,Thread thread = new Thread(); thread.start();
这段代码相信大家应该很熟悉吧,创建一个线程并且调用其start方法,接下来我们点进start方法的源码看看!
在这里插入图片描述
其底层是调用了start0这个方法,接着我们点进start0这个方法看看怎么写的。
在这里插入图片描述
我们可以看到该方法被native方法修饰了,该方法其实不是java语言的方法。
下图有助于大家理解本地方法栈的作用:
在这里插入图片描述
所以我们可以简单的理解为,本地方法栈存放的是native修饰的方法(调用接口的方法),就像上述例子的start0方法。

PC寄存器

在这里插入图片描述
了解即可!

方法区

在这里插入图片描述
了解即可!

首先看下图:
在这里插入图片描述
栈里面存放的是8中基本类型变量+对象的引用变量+实例方法都是在函数的栈内存中分配
接下来再了解什么叫做栈帧:

栈帧:可以简单的理解为在java里面就是方法,放到栈里面叫做栈帧,那么栈帧里面存放了写什么呢?
在这里插入图片描述
如果上述文字不明白可以看下图:
在这里插入图片描述
在main方法里面调用了add方法,之前讲过的实例方法放在栈里面,所以先放的mian,接着放的add方法,而add方法的本地变量等,都保存在栈帧里面。
同过上面的讲述,我们不难理解栈的运行原理,如下图:
在这里插入图片描述
在这里插入图片描述
了解完上述对栈的简析后,接下来我们看看这个错误(Error):java.lang.StackOverflowError,栈溢出,现在我们就能明白如果一个函数递归调用自己而不终止的话则会一直往栈里面添加自己,直到最后将java的栈给压满!

堆(heap):对于大多数应用来说,java堆(java Heap)是java虚拟机所管理的内存中最大的一块。Java堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。
接下来看下堆的简析:
在这里插入图片描述
物理逻辑上是新生去+养老区
注意JDK1.8之后,永久区变为元空间!

下图是对新生区的简析:
在这里插入图片描述
在这里插入图片描述
永久区的概念:
在这里插入图片描述
永久代和元空间的区别:
在这里插入图片描述
接下来讲下堆参数的调优(即JVM的调优):
可以通过下述代码查看堆的参数:
在这里插入图片描述
代码如下:

long maxMemory = Runtime.getRuntime().maxMemory();//返回Java虚拟机试图使用的最大内存量。
           Long totalMemory = Runtime. getRuntime().totalMemory();//返回Java虚拟机中的内存总量。
           System.out.println("MAX_MEMORY ="+maxMemory +"(字节)、"+(maxMemory/(double)1024/1024) + "MB");
           System.out.println("TOTAL_ MEMORY = "+totalMemory +"(字节)"+(totalMemory/(double)1024/1024) + "MB");

如何通过idea设置的参数并且查看细节呢?
Edit->Edit Configurations
在这里插入图片描述
结合上述查看堆参数的代码的打印结果如下:
在这里插入图片描述
接下来我们看看堆被撑爆的错误,如果通过上面的方法给JVM的堆分配很小的一块内存。
在这里插入图片描述
则会报错: java.lang.OutOfMemoryError :当Java虚拟机由于内存不足而无法分配对象时抛出,并且垃圾收集器不再有可用的内存。

栈、堆、方法区之间的关系

先看下图,类元数据表示的是方法内的类的模板(类的结构信)
在这里插入图片描述
再看下图结合上图理解:
在这里插入图片描述
new出来的p1,p2是存放在栈的,这是引用类型,引用的是存放在堆的new Person对象,而这些对象都是从方法区获取该类的结构信息。

猜你喜欢

转载自blog.csdn.net/Pzzzz_wwy/article/details/106609188