1.jvm(虚拟机)是程序与操作系统之间的一个桥梁。
注(个人理解):java无法直接与操作系统沟通,需要在操作系统上搭建一个虚拟机,通过程序与虚拟机沟通,并通过虚拟机传达给计算机,从而实现效果。
2.jvm的内存分配
jvm把整个内存分隔成多个区域,把每个区域划分成不同的作用
jvm内存模型主要分为:
1.本地方法栈
与虚拟机栈基本类似,区别在于虚拟机栈为虚拟机执行的java方法服务,而本地方法栈则是为Native方法服务。(栈的空间大小远远小于堆)
(1)Native方法服务:
在Java中,native方法是指使用其他编程语言(如C或C++)编写的方法,它可以在Java程序中被调用。
注(个人理解):在java中可以调用部分已经存在的c/c++语言,包括硬件特性或调用已经存在的本地库等。例如:
- String.getBytes():将字符串转换为字节数组。
- Object.hashCode():返回对象的哈希码值。
- Object.getClass():返回对象所属的类的Class对象。
2.虚拟机方法栈
描述的是java方法执行的内存模型:每个方法被执行的时候都会创建一个”栈帧”,用于存储局部变量表(包括参数)、操作栈、方法出口等信息。每个方法被调用到执行完的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。
注(个人理解):主要存储方法之间的调用关系(栈帧),栈帧主要包含堆中方法的引用地址、参数、局部变量、返回地址等。
3.程序计数器
是最小的一块内存区域,它的作用是当前线程所执行的字节码的行号指示器,在虚拟机的模型里,字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支、循环、异常处理、线程恢复等基础功能都需要依赖计数器完成。
注(个人理解):就是记录当前线程的执行位置及相关信息。
4.堆 (重要)
堆内存是所有线程共有的,可以分为两个部分:年轻代和老年代。下图中的Perm代表的是永久代,但是注意永久代并不属于堆内存中的一部分,同时jdk1.8之后永久代也将被移除。
堆是java虚拟机所管理的内存中最大的一块内存区域,也是被各个线程共享的内存区域,该内存区域存放了对象实例及数组(但不是所有的对象实例都在堆中)。
其大小通过-Xms(最小值)和-Xmx(最大值)参数设置(最大最小值都要小于1G),前者为启动时申请的最小内存,默认为操作系统物理内存的1/64,后者为JVM可申请的最大内存,默认为物理内存的1/4,默认当空余堆内存小于40%时,JVM会增大堆内存到-Xmx指定的大小,可通过-XX:MinHeapFreeRation=来指定这个比列;当空余堆内存大于70%时,JVM会减小堆内存的大小到-Xms指定的大小,可通过XX:MaxHeapFreeRation=来指定这个比列,当然为了避免在运行时频繁调整Heap的大小,通常-Xms与-Xmx的值设成一样。堆内存 = 新生代+老生代+持久代。
在我们垃圾回收的时候,我们往往将堆内存分成新生代和老生代(大小比例1:2),新生代中由Eden和Survivor0,Survivor1组成,三者的比例是8:1:1,新生代的回收机制采用复制算法,在Minor GC的时候,我们都留一个存活区用来存放存活的对象,真正进行的区域是Eden+其中一个存活区,当我们的对象时长超过一定年龄时(默认15,可以通过参数设置),将会把对象放入老生代,当然大的对象会直接进入老生代。老生代采用的回收算法是标记整理算法。
注(个人理解):堆中主要分为,年轻代与老年代还有永久代(永久代在java7开始逐渐移除,在java8中彻底移除)。jvm中最大空间区域,主要存放new出来的对象、类成员变量等。
年轻代中分为
1.伊甸园区:默认new对象开辟空间会放入这里,这里空间大小是有上限的。
2.幸存区0:当伊甸园的空间填满时,程序又需要创建对象,JVM的垃圾回收器将对伊甸园区进行垃圾回收(Minor GC),将伊甸园区中的不再被其他对象所引用的对象进行销毁。再加载新的对象放到伊甸园区,然后将伊甸园区剩余对象放入这里。
3.幸存区1:再次触发垃圾回收,此时上次存在幸存区0的内容,依旧没被回收将存放到这里,如果再次执行垃圾回收,回重新放入幸存者0区,如依旧没被回收再被放入这里。默认上线为15次(可调整),如达到上限将被存放老年区。
老年区
1.当老年区内存不足,再次触发cC: Major Gc,进行老年区清理,若老年区执行垃圾清理后,依旧无法释放足够空间,就会产生oom内存溢出(java.lang. outOfMemoryError: Java heap space)需对程序即jvm进行调优。
5.元数据区(方法区)
方法区也称”永久代“,它用于存储虚拟机加载的类信息、常量、静态变量、是各个线程共享的内存区域。默认最小值为16MB,最大值为64MB(64位JVM由于指针膨胀,默认是85M),可以通过-XX:PermSize 和 -XX:MaxPermSize 参数限制方法区的大小。
它是一片连续的堆空间,永久代的垃圾收集是和老年代(old generation)捆绑在一起的,因此无论谁满了,都会触发永久代和老年代的垃圾收集。不过,一个明显的问题是,当JVM加载的类信息容量超过了参数-XX:MaxPermSize设定的值时,应用将会报OOM的错误。参数是通过-XX:PermSize和-XX:MaxPermSize来设定的。
注(个人理解):主要存放类对象、常量池、静态成员等持久存在的。