JVM堆空间用途分析与划分依据
在上一次【https://www.cnblogs.com/webor2006/p/9876493.html】已经对JVM的内存空间的划分进行了理论化的学习,这次还是对上一次提到的理论进行进一步的补充,纯知识巩固,先来回顾一下上节JVM整个内存都由哪些构成:
下面则按照上述提到的顺序过一下:
虚拟机栈:
回顾一下上节做的笔记:
知识补充:
一个虚拟机栈它本身是归属于一上特定的线程的,换言之它是属于线程私有的内存空间。当线程开始运行的时候,与之相关的虚拟机栈就诞生了,而当线程消亡了,与之对应的虚拟机栈也就消失不见了。虚拟机栈里面的数据则为栈帧(Stack Frame),它主要是用来存储与线程操作相关的一些具体数据或者是数据结构,如之前学习的操作数栈、局部变量表,方法的出入口等,里面可以存放基本数据类型,也可以存放其引用类型(注意:引用类型不是对象!),引用类型是存储在局部变量表中,也就是放在栈帧里面的,也就是属于虚拟机栈。而对于虚拟机栈会存在StackOverFlow异常【之后会用代码来瞅一下】,也有可能出现OutOfMemory异常【但极少会出现】。
程序计数器:
回顾一下上节做的笔记:
知识补充:
它跟虚拟机栈也类似,也是属于线程私有的数据结构。在多线程的环境下,但是在同一时刻肯定只会有一个线程在执行,只是按照时间片的方式来切换线程就感觉是多个线程同时在执行一样,这里有这么一个问题:当一个线程执行到一半的时候核心切到了第二个线程了,势必要记录当前线程要挂起到什么位置上,此信息就是记录在程序计数器上的,而每一个程序计数器里面的线程执行的信息其实是线程私有的,也就是说A线程是不能得到B线程的程序计数器的。如果一个程序正在执行某一个Java方法,程序计数器记录的就是正在执行字节码对应的地址而已。
所以要记住:虚拟机栈和程序计数器是线程不共享的!
本地方法栈:
回顾一下上节做的笔记:
知识补充:
本地方法栈的主要结构其实跟虚拟机栈的结构类似,它们本质上没有特别大的区别,有些虚拟机会将本地方法栈和虚拟机栈合二为一,最典型的就是oracle的haspot虚拟机。同样它也有可能会出现StackOverFlow栈异常。
堆(Heap):
回顾一下上节做的笔记:
知识补充:
这块也是未来要花重点来透彻理解和研究的,堆内存上的对象是被所有线程所共享的,与堆相关的一个重要概念就是垃圾收集器【未来会详细来学习的】,现在几乎所有的垃圾收集器都是采用的分代收集算法,所以,堆空间也基于这一点进行了相应的划分:新生代和老年代【基本上都听说它,但也仅仅听说过】,而与之相关的还有这三个概念:Eden空间、From Survivor空间和To Survivor空间。关于新生代和老年代这里稍加解释一下,在未来会详细学习的,“一般”情况下创建了一个对象,该对象就会进入到了新生代了,新生代也是内存的一个区域,过了一段时间之后GC会对堆上的内存进行回收,由于该对像还能被GC ROOT所引用所以这一次的GC就没有被回收,接着又过一段时间GC执行该对象还是能被GC ROOT所引用所以还是木有被回收,第三次当GC又尝试进行回收该对象还是没能被回收,有可能出现了五次之后,此对象就会晋升到老年代,所谓老年代就是指里面的对象存活的比较久,而老年代垃圾回收的频率肯定是要比新生代要低很多的。
另外JVM对于Java的堆空间而言,这个空间在物理内存既可以是连续存放的,也可以是不连续的,
方法区(Method Area):
回顾一下上节做的笔记:
知识补充:
如上面描述它是用来存储元信息的,而元信息可以指类对象的基本信息、常量的一些信息等。其中说到永久代是很少会被回收的,但是不是说在方法区中的内存是不会被回收的,比如当类被卸载了相应的方法区的内存也就被回收的,只是这种情况出现得较少而已。
运行时常量池:
回顾一下上节做的笔记:
这个比较熟悉了,没啥可补充的了。
直接内存(Direct Memory):
回顾一下上节做的笔记:
知识补充:
其实这部分是跟Java NIO【关于这块还不太熟悉,待未来学习时再来体会】密切相关的,JVM通过堆上的DirectByteBuffer来操作直接内存,所以效率是比较高的。
以上相当于是对上一次知识的复习巩固,虽说枯燥但是很重要!!