上一篇讲完了java内存模型中线程私有部分(程序计数器、虚拟机栈、本地方法栈),这篇讲下所有线程公有部分
问:元空间(MetaSpace)和永久代(PermGen)的区别?
- 元空间使用本地内存,而永久代使用的是jvm的内存
jdk8以后将类的元数据放在本地堆内存中,就是元空间MetaSpace,该空间jdk以前是属于永久代的。8以后没有OutOfMemoryError:PermGen space
元空间和永久代都是存放class的相关信息,包括class和Meta、field的信息。
元空间和永久代都是方法区的实现,只是实现不同,所以说方法区只是JVM的一种规范。
JDK1.7之后原先位于方法区中的字符串常量池已经移动到了堆中。jdk8以后使用元空间(MetaSpace)替代永久代(PermGen),因为元空间划分更合理,比如说类及相关的元数据和类加载器生命周期一致,每个加载器都会分配一个单独的内存空间。
MetaSpace相比PermGen的优势
- 字符串常量池存在永久代中,容易出现性能问题和内存溢出
- 类的方法信息大小难确定,给永久代的大小置顶带来困难(太小会出现永久代溢出,太大容易导致老年代溢出)
- 永久代会为GC带来不必要的复杂性,回收效率低(永久代中的元数据的可能会随着full GC发生而进行移动,比较消耗虚拟机性能。HotSpot虚拟机的每种类型的垃圾回收器都需要特殊处理永久代中的元数据。将元数据从永久代剥离出来,不仅实现了对元空间的无缝管理,还可以简化Full GC以及对以后的并发隔离类元数据等方面进行优化)
- 方便HotSpot与其他JVM如Jrockit的集成(永久代是hotspot特有的,别的VM没有永久代)
Java堆(Heap):
- 对象实例的分配区域(heap是个大头,占用最多的内存空间。xmx设置jvm最大可用内存)
- GC管理的主要区域(所以也称为GC堆)
jvm虚拟机规范里,java堆可以使用不连续的内存空间,只要逻辑上连续即可,就像磁盘空间一样。堆空间可以是固定大小,也可以是动态扩展的(主流的虚拟机都是动态的)。如果堆中没有内存完成实例分配,且堆不能再扩展时将会抛出OutOfMemoryError异常
主流的垃圾回收机制都是采用分代收集算法,所以堆可以分为新生代、老年代,再细点就有eden区、from survivor区、to survivor区,后面博客中GC再细讲。
问:jvm三大新能调优参数 -Xms -Xmx -Xss的含义?
- -Xss:规定了每个线程虚拟机栈(堆栈)的大小(一般256k足够,此设置会影响并发线程数大小)
- -Xms:堆的初始值(就是jvm进程刚启动的时候分配的堆空间大小,后面运行时堆空间超过初始值,就会动态扩容至堆的最大空间)
- -Xmx:堆能达到的最大值(一般xms xmx 都设置成一样的,因为xms不够用时动态扩容,会发生内存抖动,影响程序稳定性)
问:java内存模型中堆和栈的区别?
首先需要明白 内存分配策略:
- 静态存储:编译时确定每个数据目标在运行时的存储空间需求(这种分配策略要求程序不允许有可变存储空间的结构存在,也不允许有嵌套、递归的结构存在。因为它们都会导致编译时无法计算准确的存储空间)
- 栈式存储:数据区需求在编译时未知,运行时模块入口前确定(动态的存储分配,是由一个类似于堆栈的运行栈实现的,和静态的分配方式相反。但是进入一个程序模块的时候必须知道该模块的存储大小,才能对其分配内存。跟栈一样按照新进后出的方式分配内存)
- 堆式存储:编译时或运行时模块入口都无法确定存储空间,需要动态分配(比如可变长度串、对象实例。堆由大片的可利用快或空闲块组成,堆中的内存可以按照任意顺序分配和释放)
堆和栈的区别:
- 联系:引用对象、数组时、栈里定义变量保存堆中目标的首地址(栈只存引用变量,类似指针,程序使用栈中的引用变量访问堆中的对象、数组。引用变量是普通变量,定义时在栈中分配,引用变量在程序运行到程序作用域之外后就会被释放掉,而数组、对象本身在堆中分配,即使程序运行到作用域之外(new或者产生数组的代码块之外)堆中的数组、对象的内存不会被释放,之后会被垃圾回收掉)
- 管理方式:栈自动释放,堆需要GC(JVM自己可以针对内存栈进行管理操作,而且该内存空间的释放是编译器就可以操作的内容。而堆空间在java中,jvm执行引擎是不会对其释放的操作,而是让GC回收)
- 空间大小:栈比堆小(栈里面存储的数据和本身需要的数据特性决定的。堆需要存储比较多的对象数据,一般都很大)
- 碎片相关:栈产生的碎片远小于堆(堆空间的活动空间相当于栈比较大,可能存在长期的内存分配和释放操作,而且GC不是实时的,这使得堆中的碎片逐渐累积起来。栈空间本身就是堆栈数据结构,它的操作都是一一对应的,而且内存结构相对于堆空间的简单,不容易产生碎片)
- 分配方式:栈支持静态和动态分配,而堆仅支持动态分配
- 效率:栈的效率高于堆(因为内存块本身就是个堆栈的结构,和栈空间的结构相符合,操作也简单,只存在入栈出栈两个操作。相对于堆空间,栈空间的灵活程度不够,特别是动态管理的时候,而堆空间最大优点就是动态分配,因为它在计算机底层可能是个双向链表的结构,所以管理的时候操作比栈复杂很多,所以堆的效率低于栈,但是灵活度高了很多)
例子:
**问:不同JDK版本的intern()方法的区别?(主要是JDK6 VS JDK6+)**String.intern()是一个Native方法,底层调用C++的 StringTable::intern
方法,源码注释:当调用 intern 方法时,如果常量池中已经该字符串,则返回池中的字符串;否则将此字符串添加到常量池中,并返回字符串的引用。
例子:
JDK6的时候存在永久代,所以VM options可以设置PermSize大小
运行就会内存溢出,这也证明了JDK6存在永久代且永久代里面由字符串常量池
JDK7 永久代移到堆中了 所以没有内存溢出的错误
JDK8 也能输出Mission Complete 但是没有永久代的设置了
intern在jdk6和6+的区别
jdk7+是
jdk6是
详情请看:https://www.jianshu.com/p/0d1c003d2ff5 或者 https://tech.meituan.com/2014/03/06/in-depth-understanding-string-intern.html