1.内存模型
1.1.JVM 内存模型的介绍
内存模型主要分为五个部分:虚拟机栈,本地方法栈,堆,方法区(永久代或元空间),程序计数器,当然还有一部分是直接内存。
**虚拟机栈:**每个线程各有一个,线程独有,当执行方法(除了本地方法 native 修饰的方法)之前,会创建一个栈帧,栈帧里面包含局部变量表和操作数栈和动态链接和方法出口等信息,而每个栈帧就是存入栈中。
**本地方法栈:**每个线程各有一个,线程独有,当执行本地方法时类似于虚拟机栈,一样会创建栈帧,存入对应信息。
**程序计数器:**每个线程各有一个,线程独有,它的作用是记录当前线程下一次执行的二进制字节码指令地址,如果执行的是本地方法那么它会记录为定值(null)。
**堆:**所有线程共享,堆的回收由垃圾回收机制管理,堆中主要存入对象实例信息,类信息,数组信息,堆是 JVM 内存中最大的一个。
**永久代:**在 JDK 1.7 及以前是方法区的实现,使用的是 JVM 内存,独立于堆,主要存入类信息,静态变量信息,符号引用等信息。
**元空间:**在 JDK 1.8 及以后是方法区的实现,使用的是本地内存,主要存入类信息,静态变量信息,符号引用等信息。
**直接内存:**该内存属于操作系统,由 NIO 引入,操作系统和 Java 程序都可以进行操作,实现共享。
**常量池:**属于 class 文件的一部分,主要存储字面量,符号引用。
**运行时常量池:**属于方法区,其实就是将常量池中的符号引用替换成了直接引用,其余一样。
1.2.堆和栈的区别
五个点:用途,生命周期,存储速度,存储空间,可见性。
**用途:**栈主要存储方法返回地址,方法参数,临时变量,每次方法执行之前会创建栈帧,而堆存储对象的实例信息,类实例信息,数组信息。
**生命周期:**栈的生命周期可见,每次方法执行完栈帧就会移除(弹出),而堆中的数据需要由垃圾回收器回收,回收时间不确定。
**存储速度:**栈的速度更快,栈保持"先进后出"的原则,操作简单快,而堆需要对对象进行内存分配和垃圾回收,并且垃圾回收器本身运行也会损耗性能,速度慢。
**存储空间:**栈的空间相对于堆的空间小,栈的空间小且固定,由操作系统管理,而堆的空间是 JVM 中最大的,由 JVM 管理。
**可见性:**栈是每个线程都有的,而堆是所有线程共享的。
1.3.栈的存储细节
如果执行方法时,里面创建了基本类型,那么基本类型的数据会存入栈中,如果创建了引用类型,会将地址存入栈,其实例数据存入堆中。
1.4.堆的部分
堆主要分为两部分:新生代,老年代,它的比例:1:2。
新生代:新生代分为两个区:伊甸园区和幸存者区,而幸存者区又平均分为 S0 和 S1区,伊甸园区与 S0 与 S1 之间的比例:8:1:1,每次新创建的对象实例都会先存入伊甸园区,它们主要使用的垃圾回收算法是复制算法,当伊甸园区的内存使用完时,会使用可达性分析算法,标记不可存活的对象(没有被引用的对象)将存活对象复制移入 S0 或 S1 中,这个过程叫Minor GC,如果这次移入的是 S0,那么下次就会将伊甸园区和 S0 中的对象移入 S1 中,循环反复,每经历一次 Minor GC 过程就会给对象年龄加一,直到大于等于 15 时,会认为该对象生命周期长,移入老年代中。
细节:其实新创建的对象不会直接存入伊甸园区,如果多线程情况下同时进行存入对象(线程竞争压力大)会导致性能的损失,因此会给每个线程从伊甸园区中先申请一块TLAB区域,先将对象存入该区,如果该区内存使用完,会重写申请或直接存入伊甸园区。
**老年代:**老年代就是存储生命周期长的对象(不经常回收的对象),主要使用的垃圾回收算法为标记清除算法或标记整理算法,看场景出发,其中老年代还包含一个大对象区。
**大对象区:**主要存储的就是新创建的大对象比如说大数组,会直接将该对象存入大对象区中,不在存入新生代。
**可达性分析算法:**从 GC Root 出发找对应引用对象,如果一个对象没有被直接引用或间接引用,那么会被标记,GC Root 可以是 java 的核心库中的类,本地方法使用的类,还未结束的线程使用的类,使用了锁的类。
**标记清除算法:**对引用的对象进行标记,然后进行清除(不是真正的清除,而是记录其对象的起始地址和结束地址到一个地址表中,下次要添加新对象时会先从表中找,找到一个适合大小的就会进行覆盖),清除:记录地址,新对象进行覆盖,好处:速度快,缺点:内存碎片化严重(内存不连续了,本来可以存入的对象存入不了)。
**标记整理算法:**同理进行标记,然后再对可存活对象进行整理,最后清除,好处:避免了内存碎片化问题,缺点:速度慢。





