虚拟机运行时数据区堆,栈,方法区(下篇)
铁汁们,可以先去看看上篇哟点击传送
概述
在上篇简单概述了虚拟机运算时数据区里的 pc寄存器,本地方法栈,虚拟机栈,以及虚拟机栈里栈帧的结构,这些都是属于线程的私有空间,每个线程都会有这些结构,但是下篇讲的堆和方法区是所有线程共享的。
内存图示
堆
在堆中内存被分为两大块,分别是年轻代(YongGen)和老年代(OldGen),他们都是用来存储java对象的,但是在jvm中java对象被分为两类:第一类是生命周期较短,朝生夕死的那类对象,这类对象的创建和消亡都非常迅速,所以被放在年轻代中。第二类对象的生命周期非常长,这类对象就被放在老年代。
堆内存图示
垃圾回收:
(就是当虚拟机内存不够时,就会启动垃圾回收器进行扫描,把那些没用的对象就会清理掉)
年轻代里又分为几个空间分别是Eden,和两个Survivor区。当我们对象刚创建时会在Eden里,但当对象经历过一次垃圾回收,但被认为不是垃圾的时候就会被放入Survivor区,然后每次垃圾回收时Eden幸存的对象和一个Survivor区中的幸存对象就会被放入另外一个Survivor区里周而复始,如果对象经历了一定次数(可以设置,默认15次)的垃圾回收后,就会被放入老年代中。
堆区总结
堆空间的Eden区存放刚出生的对象,Survivor区中存放垃圾回收后幸存的对象,老年代中存放那些特别持久的对象。为什么会这样分区呢,可以理解为因为垃圾回收器经常回收年轻代,很少回收老年代,所以当我们判断一个对象可以存活很久的时候就放入老年代,这样下次垃圾回收就不用判断此对象是否需要回收了,提高了性能。
方法区
方法区中存储什么
类型信息:
对每个加载的类型(类class,接口interface,枚举enum,注解annotation),jvm必须在方法区中存储以下类型信息。
这个类型的完整有效名称(全名=包名+类名)。
这个类型直接父类的完整有效名
这个类型的修饰符(public ,abstract,final的某个子集)
这个类型直接接口的一个有序列表
域(Feid)信息:
jvm必须在方法区中保存类型的所有域的相关信息以及域的声明顺序
域的相关信息包括:域名称,域类型,域修饰(public , private,protected,static,final,volatile,transilet的某个子集)
方法信息:
jvm必须保存所有方法的以(public , private,protected,static,final,volatile,transilet的某个子集)下信息,同域信息一样包括声明顺序
方法名称
方法返回值
方法参数 数量和类型
修饰符(public , private,protected,static,final,synchronzed,native,abstract的一个子集)
方法字节码,操作数栈,局部变量表及大小
异常表(abstract和native方法除外)
每个异常处理的开始位置,结束位置,代码处理在程序计数器中的偏移位置,被捕获异常类的常量池索引。
运行时常量池
运行池中包括:
数据值,字符串值,类引用,字段引用,方法引用
运行时常量池是方法区的一部分
常量池在类加载后存放在方法区的运行时常量池中
jvm为每个已加载的类型(类或接口)都维护一个常量池,池中的数据像数组项引用,通过索引访问。
运行时常量池保护多种不同的变量,包括编译器就已经明确的数值字面量,也包括运行期解析后才获得的方法或者字段引用,此时不再是常量池中的符号地址了,这里换为真实地址
方法区的演进
jdk1.6及之前。有永久代。静态变量存在永久代
jdk1.7 有永久代,但逐步地去除 永久代 字符串常量池 ,静态变量移除。保存在堆中
jdk1.8以后 无永久代 ,类型信息,字段,常量保存在本地内存的元空间。但字符串常量池。静态变量仍在堆。
方法区总结
方法区是存放类型一切信息的一个空间,在方法的演进里说明了方法区的进化过程,在jdk1.8后,它不在使用虚拟机的内存空间了,而是使用本地内存的元空间,这样我们可以存放的类型就多一些和少一些都没关系,不用去考虑方法区大小的问题了。
秃头萌新 多多关照