JVM底层原理-深入理解内存结构各个区域的作用

1.JVM底层原理-深入理解内存结构各个区域的作用

1.1 内存结构

总的来看,内存可以分为线程共享和线程不共享的区域,线程共享了堆,方法区,执行引擎和本地库接口。线程不共享的区域是栈,程序计数器和本地方法栈。
JVM内存结构图

1.2 程序计数器

1.2.1 作用

通过改变计数器里面的值,每个值指向下一个要执行的语句。

1.2.2 注意

在单核内,每一段时间只能有一个线程在运行,每个线程的执行语句都不一样,所以为了在上下文切换的过程中迅速找到分到时间片的线程的执行语句位置,程序计数器都是线程独享的。

1.3 虚拟机栈

1.3.1 栈工作方式

虚拟机栈在执行每个方法时,创建一个栈帧,大部分用来装局部变量表,周期就是整个方法。在方法开始时,将该栈帧压栈,方法结束时从虚拟机pop出该栈。

局部变量表中每个局部变量所占用的空间在编译期间已经分配好了。

1.3.2 栈溢出

栈溢出有两种情况
a.当请求栈的深度大于栈本来的深度:比如在递归时不断调用方法,前一个方法还没结束,自然无法出栈,递归的下一个方法压入栈,长此以往导致栈溢出,StackOverflow异常
b.允许扩展的情况下,扩展的栈无法申请到内存的时候,OOM异常。

1.4 本地方法栈

本地方法栈与虚拟机栈作用类似,只不过虚拟机栈执行java程序内的方法,本地方法栈执行Native 方法。

1.5 堆(唯一目的是存放对象实例)

1.5.1 内存溢出

当请求内存时,内存已经不够,会产生内存溢出。

1.5.2 内存泄漏

请求后的内存无法回收,产生内存泄露。

1.6 方法区

1.6.1 常量池

常量池分为Class文件常量池和运行时常量池。

Class文件常量池在编译之后就已经存储在Class文件中,运行时常量池在运行时可能还会有新的常量进入池子(这里注意:先编译成class文件再进行类加载)

1.6.2 元空间

在JDK7之后,常量池和静态变量就已经从永久代移动到了堆中,JDK8后,永久代其他东西都被移动到了元空间。
元空间是本地内存,不在java堆中,其中的数据在收集过程中分别经过标记,卸载,经过JVM的类卸载器来进行垃圾处理而不是JVM的垃圾收集器。

元空间对比永久代优势

1、 根据类加载过程可知,永久代在类加载时就根据class文件中类型的数据分配了一个固定的空间,无法动态分配,而元空间可以在不影响JVM工作的情况下动态改变。
2、 元空间通过指针来表示存储于其中的类数据等,而不是引用的方式,提高了性能
3、 元空间可以通过JVM参数来修改空间大小

1.6.3 String.intern方法

该方法是一个本地方法,判断某个字符串是不是存在于常量池,不存在就将其加入到常量池。在JDK7以前可以通过不断往常量池中加入常量的方法来引起常量池溢出异常,但是JDK7后将常量池从永久代放入了堆,则不会引起异常。
在JDK6中,intern会把第一次出现的实例常量复制一份放入常量池并返回它的引用,和StringBuffer创建在堆中的字符串当然不是一个东西,JDK7后就是一个东西了,intern只会标记一下第一次出现的实例。

1.7 直接内存

占内存中很小一部分,但是被忽视容易造成OOM错误,JVM和OS交互过程中,因为存在内存屏障,当有数据需要跨过屏障时,只能copy一份到对面的内存,这样子就有两份数据需要维护,这个时候如果能开辟直接内存供JVM存储在堆里的引用间接访问,那么就只有一份数据需要维护了,减小了开销。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值