加载中...
加载中...
Java的堆,栈,方法区

Java的堆,栈,方法区

关于java内存区域部分的堆,栈,方法区三个部分而言,我总结了一下大概为一下这张图。  


栈(Stack):

1.为什么栈要用来存储基本变量信息和对象引用

java虚拟机的基本架构就是采用栈来进行设计的。当一个程序需要运行的时候,由于要预先内存空间和运行的生命周期,所以需要进行指针的变动,来进行内存大小的分配。是的,由于这个操作会对程序的执行带来一定的不方便,所以一般栈被用来存放一些基本的变量类型或者引用对象的地址,而对于存储数据量较为庞大的java对象责备存储在了堆里面了。

2.为什么说栈的提取速度比堆要快?

我对于硬件部分也不是很理解,所以这里也只好从软件方面来分析,总结分析之后得出原因有以下几个:
1.栈里面的内存大小一般都是程序启动的时候由系统分配好的。
2.堆的内存大小需要在使用的时候才回去申请,而且每次对于内存大小的申请和归还都会比较消耗性能,开销较大。
3.cpu里面会有专门的寄存器来操作栈,堆里面都是使用间接寻址的方式来进行对象查找的,所以栈会快一些。

3.虚拟机栈里面为什么会有数据共享一说?

这个原因我们可以举一个例子来说吧:
假设有这么一段代码:
int j=10;
int k=10;
j=12;
对于这段简单的代码而言,虚拟机里面的栈又是如何处理的呢?其实比较简单的理解就是如下所示:
首先在栈里面会先查找是否存在一个区域存放有10,如果没有就创建一个区域存放10。然后将j引用到这个变量里面去。接着当程序执行到第二句的时候,虚拟机会先到栈里面去查找一下是否存在一个值为10的区域,如果有就直接让k指向10。

当后期对于j变量的值进行修改为12以后,栈就会重复性的先去查找是否有12,如果没有开辟新的空间用于存储12,然后让j指向这个值 

这里面的10这个值,被k,j共同引用的过程就叫做数据共享。

4.栈里面到底存储了什么?

关于这个问题,我在《深入理解jvm&gc》这本书里面看到了一个比较好的解释,栈里面存储了很多小项,这里面我们称之为栈帧,这是一种数据结构用于存储方法的局部变量表,操作数栈,动态链接方法还有上下文数据等信息。
那么我们怎么来理解这个栈帧呢?每个栈帧里面都会存储有相应的一下内容:
1.局部变量表
2.操作数栈
3.动态链接
4.返回地址
这些东西第一次听说一般都会感觉比较陌生,那么我们来逐个逐个一一讲解:

局部变量表:
这里面的作用主要是存储一系列的变量信息,而且这些变量都是以数字数组的形式来存储的,一般而言byte,short,char,类型的数据在存储的时候会变为int类型,boolean类型也是存储为数字类型,long,double则是转换为双字节大小的控件存储在栈里面。

操作数栈:
关于这个小编也是有点不太了解,看了几篇博客之后也只是知道这个东西是可以将指令在栈里面进行push和pop操作,也是一个数字数组类型。
关于详情的分析可以去阅读以下这篇博客:
https://blog.csdn.net/qq_28666081/article/details/85269879

动态链接:
动态链接的作用主要还是提供栈里面的对象在进行实例化的时候,能够查找到堆里面相应的类地址,并进行引用。这一整个过程,我们称之为动态链接。

返回地址:
某个子方法执行完毕之后,需要回到主方法的原有位置继续执行程序,方法出口主要就是记录该信息

堆(heap)

1.堆里面存放的内容主要还是new出来的对象和一些数组信息。

2.java的虚拟机不需要知道从堆内存里面存放多少空间大小的变量信息,也不需要知道每个对象的生命周期,所以一般程序运作的灵活性很高。

3.堆区里面存放了大量的对象很信息,所以也成为了gc重点回收的一个区域模块,所以当大量内存被占用的时候,gc的垃圾回收就会成为整个系统的性能瓶颈。于是随着jdk的不断更新,新的技术也对于jvm的内存分配这一块进行一定的优化改善,实现了off-heap。

好了,知道这些内容之后,基本简单的面试问题都可以干掉了,如果要深入了解的话可以从以下几个方面入手:

关于垃圾回收的原理和分类?
可以看看我的另一篇文章:https://blog.csdn.net/Danny_idea/article/details/87475188
什么是指针逃逸?如何对此进行优化?
可以参考我的公众号文章:https://mp.weixin.qq.com/s/tyF9Ve40C_uu4rwcs4tUsg

静态方法区:
关于这个概念也可以称之为静态区,静态区和堆很相似,里面存放的信息也是线程共享的,它包含的信息如下图所示:

方法区中包含的都是在整个程序中永远唯一的元素,如class,static变量。

方法区又被称为静态区,是程序中永远唯一的元素存储区域。和堆一样,是各个线程共享的内存区域。它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。

Java虚拟机规范对方法区的限制非常宽松,除了和Java堆一样 不需要连续的内存和可以选择固定大小或者可扩展之外,还可以选择不实现垃圾回收。
这区域的内存回收目标主要是针对常量池的回收和类型的卸载,一般而言,这个区域的内存回收比较难以令人满意,尤其是类型的回收,条件相当苛刻,但是这部分区域的内存回收确实是必要的。

很多开发者更愿意把方法区称为“永久代”(Perm Gen)(Permanent Generation)「总是存放不会轻易改变的内容」。在目前已经发布的JDK 1.7 的HotSpot中,已经把原本放在永久代的字符串常量池移至堆中。

运行时常量池(Runtime Constant Pool)是方法区的一部分。

JDK 1.8 中,已经没有方法区(永久代),而是将方法区直接放在一个与堆不相连的本地内存区域(Native Memory),这个区域被叫做元空间。

原文:https://blog.csdn.net/danny_idea/article/details/81137306


没有更多推荐了 [去首页]
image
文章
376
原创
293
转载
83
翻译
0
访问量
183399
喜欢
73
粉丝
5
码龄
7年
资源
3

文章目录

加载中...
-1
2