APP下载
报价宝  ›  科技  › 

深入理解JAVA虚拟机器器学习笔记-----JVM内存模型

报价宝 来源:baojiabao.com 发布时间:2019-07-05 13:51:00 09月10日更新
报价宝综合消息深入理解JAVA虚拟机器器学习笔记-----JVM内存模型

一、JVM内存模型概述

JVM内存模型其实也挺简单的,这里先提2个知识点:

1、组成:java堆,java栈(即虚拟机器栈),本地方法栈,方法区和程式计数器。

2、是否共享:其中方法区和堆区是执行绪共享的,虚拟机器栈,本地方法栈和程式计数器是执行绪私有的,也称执行绪隔离的,每个区域储存不同的内容。这2个知识点必须牢记,是掌握JVM内存模型的基础。

二、程式计数器

JVM中的程式计数器是一块很小的内存区域,但是这块内存区域挺有意思的。主要特性有3个:

1、储存内容:对于java普通方法(即没用native关键字修饰的方法),储存的是执行过程中当前指令的地址,而对于native方法,这里是空的(undefined),为啥呢?因为呼叫本地方法的时候可能已经超出了JVM虚拟机器的内存地址了。

2、执行绪私有的:为什么程式计数器是执行绪私有的?根据储存内容也好理解,假如是执行绪共享的,那多个执行绪执行的时候,都不知道自己当前执行绪执行的地址是哪个了,有的执行绪快,有的执行绪慢,快的执行完就进入下一步,等慢的执行绪执行完回来发现自己的地址都变了,岂不乱套?

3、是JVM中唯一不会报内存溢位(OutOfMemoryError)的区域。

三、虚拟机器栈

虚拟机器栈主要储存的是一个个栈帧,每个栈帧中储存的是区域性变量表,算子栈,动态连结和方法出口资讯等。其中区域性变量表中储存的是方法中定义的一些区域性变数,物件的引用,引数,和方法的返回地址等。区域性变量表所占用的空间大小在编译期就能确定,在方法执行的时候,并不会改变区域性变量表的空间大小,这结合区域性变量表储存的内容就很好理解。算子栈可以理解为对当前操作的资料入出栈,对于64位长度的long和double型别,每个算子占用2个字宽(slot),其他型别的算子占用一个字宽(slot)。每个方法呼叫时都会建立一个栈帧,执行的过程对应的就是一个栈帧在虚拟机器栈中从入栈到出栈的过程。有关栈帧的内容可以参考一个网友写的一篇部落格:https://blog.csdn.net/xtayfjpk/article/details/41924283,讲的很好很详细。这里放个栈帧的图,看了一目了然。

关于虚拟机器栈内存溢位有2种情况:

1、执行绪请求的栈深度 超过了虚拟机器允许的深度,会丢掷StackOverflowError,所以当我们在程式码中看到这个异常时,就应该想到可能是虚拟机器栈出了问题。

2、如果虚拟机器栈可以动态扩充套件(当前大部分JVM都可以动态扩充套件,不过JVM也允许固定长度的虚拟机器栈),当扩充套件时无法申请到足够的内存时,会丢掷OutOfMemoryError异常。

四、本地方法栈

这块知识点比较简单,本地方法栈和虚拟机器栈的功能类似,只不过是为JVM呼叫native方法时服务的,而且JVM对本地方法使用的语言(比如Java呼叫C语言实现的功能,就需要定义native方法来实现)、使用方式和资料结构都没有强制规定,因此不同的虚拟机器可以自由实现。而且HotSpot虚拟机器直接把本地方法栈和虚拟机器栈合二为一。与虚拟机器栈类似,本地方法栈也会丢掷StackOverflowError和OutOfMemoryError。

五、方法区

方法区是一个比较重要的区域,java虚拟机器规范中把方法区描述为堆的一个逻辑部分,但是为了和Heap(堆区)对应,也称Non-Heap(非堆区)。主要储存的是静态变数,常量(包括执行时常量),类的载入资讯和java编译后的程式码。这部分空间不需要连续,可以选择固定大小和可扩充套件,通常在这部分是没有GC的,因为GC回收的都是些静态变数,常量和类的载入资讯,这些物件回收效果通常不尽人意,因此可以选择不实现垃圾回收。这块区域也称为持久代,当这块内存不足时,也会报OutOfMemoryError异常。

六、堆区

Java堆区是JVM内存中最胖的一块区域,因为这里储存的都是物件的例项和阵列物件。这块区域是执行绪共享的,在JVM启动时就会建立,想想如果这么大的空间是执行绪私有的,那内存不得爆掉吗?按照java虚拟机器规范,堆区的内容可以物理上不连续,只要逻辑上连续即可,在实现时可以是固定大小的,也可以是可扩充套件的,而且通常都是可扩充套件的,我们常用的内存引数-Xms和-Xmx就是用来调节堆大小的。java堆区按生命周期不同,分为新生代和老年代。新生代又可以细分为Eden和Survivor区,而Survivor又可以细分为Survivor1和Survivor2,这两者通常只使用其中一块,另一块用来GC时保留存活的物件。大部分的new出来的物件都是存放在Eden区,如果是大物件,比如一个很大的阵列或者List物件,可以通过JVM引数-XX:PretenureSizeThreshold将超过指定大小的物件直接存入到老年代,需要注意的是,写程式时应该尽量避免朝生夕死的大物件进入老年代,因为相比年轻代的GC,老年代GC的成本更大。Eden和Survivor的预设大小比值的8:1:1,新生代预设的GC算法是复制算法。老年代的预设GC算法是标记整理法。关于这2种GC算法,会在下篇部落格讲解。

当堆中没有足够内存时,会丢掷OutOfMemoryError异常。关于堆区的内存模型,可以参考下面的图片:

Java资料获取:转发+私信“Java”即可

文章标签: 报价宝 降噪耳机价格 耳机价格 红米手机价格 华为手机价格 小米手机价格 电视机价格 笔记本电脑价格 笔记本价格 汽车价格 报价宝 笔记本价格 笔记本电脑价格 电视机价格 小米手机价格