JVM基础知识
1. 在Java中,程序员把内存控制权利交给 Java 虚拟机而不用像c++为一个 new 操作去写对应的 delete/free 操作,因此不是很容易出现内存泄露和溢出的问题。
Java 虚拟机在执行 Java 程序的过程中会把它管理的内存划分成若干个不同的数据区域
其中线程私有的有:虚拟机栈,本地方法栈,程序计数器;线程共共享的有堆,方法区,直接内存。
2. 程序计数器的作用:字节码解释器通过改变程序计数器的值来选取下一条需要执行的字节码指令,从而实现代码的流程控制;在多线程的情况下,程序计数器用于记录当前线程执行的位置,从而当线程被切换回来的时候能够知道该线程上次运行到哪儿了。
注意:程序计数器是唯不会出现 OutOfMemoryError 的内存区域,它的生命周期随着线程的创建而创建,随着线程的结束而死亡。
3. Java虚拟机栈:
Java虚拟机栈是由一个个栈帧组成,而每个栈帧中都拥有局部变量表、操作数栈、动态链接、方法出口信息。每个方法从被调用,直到被执行完。对应着一个栈帧在虚拟机中从入栈到出栈的过程
Java 虚拟机栈会出现两种异常:StackOverFlowError 和 OutOfMemoryError。第一个是如果线程请求的栈深度大于虚拟机所允许的深度抛出的错误,第二个则是虚拟机在动态扩展栈时无法申请到足够的内存空间抛出的异常。
通常说的栈就是指局部变量表部分,存放编译期间可知的8种基本数据类型,及对象引用和指令地址。局部变量表是在编译期间完成分配,当进入一个方法时,这个栈中的局部变量分配内存大小是确定的。jvm 的局部变量表是做什么的?存储局部变量、函数调用时传递参数,很多字节码指令都是对局部变量表和操作数栈进行操作的。
4. 本地方栈:
该区域与虚拟机栈所发挥的作用非常相似,只是虚拟机栈为虚拟机执行 Java 方法服务,而本地方法栈则为使用到的本地操作系统(Native)方法服务,和平台有关的方法。
5. 堆
Java 虚拟机所管理的内存中最大的一块,Java 堆是所有线程共享的一块内存区域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例以及数组都在这里分配内存。同时它也是是垃圾收集器管理的主要区域,因此也被称作GC堆,具体的虚拟机怎么进行GC,可以看https://www.cnblogs.com/jkzr/p/10572606.html。Java堆还可以细分为:新生代和老年代:再细致一点有:Eden空间、From Survivor、To Survivor空间等。进一步划分的目的是更好地回收内存,或者更快地分配内存。
新生代分为:1个Eden区和2个Survivor区(分别叫from和to)。默认比例为8:1。参考https://blog.csdn.net/lojze_ly/article/details/49456255
一般情况下,新创建的对象都会被分配到Eden区(一些大对象特殊处理),这些对象经过第一次Minor GC后,如果仍然存活,将会被移到Survivor区。对象在Survivor区中每熬过一次Minor GC,年龄就会增加1岁,当它的年龄增加到一定程度时,就会被移动到老年代中。
在GC开始的时候,对象只会存在于Eden区和名为“From”的Survivor区,Survivor区“To”是空的。紧接着进行GC,Eden区中所有存活的对象都会被复制到“To”,而在“From”区中,仍存活的对象会根据他们的年龄值来决定去向。年龄达到一定值(年龄阈值,可以通过-XX:MaxTenuringThreshold来设置)的对象会被移动到年老代中,没有达到阈值的对象会被复制到“To”区域。经过这次GC后,Eden区和From区已经被清空。这个时候,“From”和“To”会交换他们的角色,也就是新的“To”就是上次GC前的“From”,新的“From”就是上次GC前的“To”。不管怎样,都会保证名为To的Survivor区域是空的。Minor GC会一直重复这样的过程,直到“To”区被填满,“To”区被填满之后,会将所有对象移动到年老代中。
新生代和老年代的一些区别:新生代和老年代是针对于分代收集算法来定义的,新生代又分为Eden和Survivor两个区。加上老年代就这三个区。数据会首先分配到Eden区 当中(当然也有特殊情况,如果是大对象那么会直接放入到老年代(大对象是指需要大量连续内存空间的java对象)。),当Eden没有足够空间的时候就会 触发jvm发起一次Minor GC。如果对象经过一次Minor GC还存活,并且又能被Survivor空间接受,那么将被移动到Survivor空 间当中。并将其年龄设为1,对象在Survivor每熬过一次Minor GC,年龄就加1,当年龄达到一定的程度(默认为15)时,就会被晋升到老年代 中了,当然晋升老年代的年龄是可以设置的。其实新生代和老年代就是针对于对象做分区存储,更便于回收等等
有关年轻代的几个参数:
1.XX:NewSize和-XX:MaxNewSize
用于设置年轻代的大小,建议设为整个堆大小的1/3或者1/4,两个值设为一样大。
2.XX:SurvivorRatio
用于设置Eden和其中一个Survivor的比值,这个值也比较重要。默认8:1;
3.XX:+PrintTenuringDistribution
这个参数用于显示每次Minor GC时Survivor区中各个年龄段的对象的大小。
4-XX:InitialTenuringThreshol和-XX:MaxTenuringThreshold
用于设置晋升到老年代的对象年龄的最小值和最大值,每个对象在坚持过一次Minor GC之后,年龄就加1。
6. 方法区:
它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据,它有一个别名叫做 Non-Heap(非堆)也常称为永久代,垃圾收集行为在这个区域是比较少出现的,但并非数据进入方法区后就“永久存在”了。
7. 对象的创建:
分配内存有“指针碰撞” 和 “空闲列表” 两种。
对象的访问也有句柄和直接指针两种。