JVM内存模型:程序计数器,虚拟机栈,本地方法栈,Java堆,方法区
程序计数器:程序计数器是一块较小的内存空间,它的作用可以看做是当前线程所执行的字节码的行号指示器。字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成。
虚拟机栈:与程序计数器一样,Java 虚拟机栈(Java Virtual MachineStacks)也是线程私有的,它的生命周期与线程相同。虚拟机栈描述的是Java 方法执行的内存模型:每个方法被执行的时候都会同时创建一个栈帧(StackFrame)用于存储局部变量表、操作栈、动态链接、方法出口等信息。
本地方法栈:本地方法栈(Native Method Stacks)与虚拟机栈所发挥的作用是非常相似的,其区别不过是虚拟机栈为虚拟机执行Java方法(也就是字节码)服务,而本地方法栈则是为虚拟机使用到的Native 方法服务。
Java堆:几乎所有的对象和数组都是在堆中分配空间的,分为新生代和老年代。新生代可分为eden, survivor space 0, survivor space1
方法区:方法区(Method Area)与Java堆一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
在Hot spot虚拟机中,方法区也叫永久区,但是也会被GC, GC主要有两类:对常量池的回收,对类元数据的回收
**如果VM确认所有该类的实例都被回收并且装载该类的类加载器也被回收了,那么就回收该类的元数据
**运行时常量池(Runtime Constant Pool)是方法区的一部分。(不一定)
Java语言并不要求常量一定只能在编译期产生,也就是并非预置入Class文件中常量池的内容才能进入方法区运行时常量池,运行期间也可能将新的常量放入池中,这种特性被开发人员利用得比较多的便是String类的intern()方法。
JVM参数:设置最大堆内存Xmx,最小堆内存Xms,新生代大小Xmn,老年代大小PermSize,线程栈大小Xss,新生代eden和s0空间大小比例以及老年代和新生代的空间大小比例
垃圾回收算法
(1)引用计数法:缺点是无法处理循环引用问题
(2)标记一清除法:标记所有从根节点开始的可达对象,清除所有未被标记的对象。缺点是会造成内存空间不连续,不连续的内存空间的工作效率低于连续的内存空间,不容易分配内存
(3)复制算法:将内存空间分成两块,每次将正在使用的内存中的存活对象复制到未使用的内存块中,算法效率高,但是代价是将系统内存折半。适用于新生代(存活对象少,垃圾对象多)
(4)标记一压缩算法:标记一清除的改进,清除未标记的对象时还将所有的存活对象压缩到内存的一端既避免碎片产生,又不需要两块同样大小的内存块,性价比高。适用于老年代
(5)分代:把堆分成两个或者多个子堆,每一个子堆被视为一代。算法在运行的过程中优先收集那些“年幼”的对象,如果一个对象经过多次收集仍然“存活”,那么就可以把这个对象转移到高一级的堆里,减少对其的扫描次数。
垃圾回收器的类型
(1)线程数:串行,并行
(2)工作模式:并发,独占
(3)碎片处理:压缩,非压缩
(4)分代:新生代,老年代
并行:开启多个线程同时进行垃圾回收,缩短GC停顿时间
并发:垃圾回收线程和应用程序线程交替工作
CMS: Concurrent Mark Sweep并发标记清除,减少GC造成的停顿时间过程:初始标记,并发标记,重新标记,并发清理,并发重置
G1:基于标记-整理算法