jvm内存划分
jvm内存分为线程私有、线程共享、直接内存
线程私有包括:程序计数器,虚拟机栈,本地方法栈
线程共享包括:堆,方法区
程序计数器:每条线程都要有一个独立的程序计数器,cpu通过这个来决定执行的线程,根据这个取指
虚拟机栈:之前上java棵老师提过一嘴,java的方法是放在栈里面的,这里的栈就是指虚拟机栈,该有就是存放局部变量等等
本地方法栈:用来放一些naive方法,这些方法通常是用c或c++写的,因为有时候java方法效率太低了,就看做特殊的虚拟机栈来处理
方法区(永久代):提一嘴永久代在java8之后变成元数据,而元数据不在虚拟机内存中在本地内存中,方法区用于存储被JVM 加载的类信息、常量、静态变量、即时编译器编译后的代码等数据,另外运行时常量池也在这里面
堆:放实例化的对象还有数组,new完的对象就放里面,是javaGC(垃圾清除)机制的重点区域
jvmGC机制
如何确定垃圾?
- 引用计数法:就是看他的引用有多少,为0即可回收
- 可达性分析:简单地说就是以这个即将被回收的对象出发,看看有没有其他对象指向,没有就被标记为不可达对象,但是请注意不可达对象变为可回收对象至少要经过两次标记过程。两次标记后仍然是可回收对象,则将面临回收。
垃圾回收算法
- 标记清理
- 复制回收
- 标记整理
- 分代收集
标记清理
简单的一种,把最垃圾标记起来然后清理掉,缺点是内存碎片化(比如有些对象需要站4格连续的但是你最大只有两格连续的,自然就浪费了)
复制算法
把内存分成两半,平时只用一半,清理时把有用的有序放在另一半,然后清除原来那一半,缺点很明显,空间利用率低
标记整理法
是上面两种结合,看图就懂了
分代收集
不同的区的垃圾回收侧重可能不同,造成效率不同,这个请看下面对堆的垃圾回收分析你就懂了
示例:堆的垃圾回收
上面提到了堆是重点GC的区域,那我们再深挖一下堆的空间结构
首相分为新生代和老年代两部分
新生代又分Eden区,surviveFrom和surviveTo区
Eden 区:Java 新对象的出生地(如果新创建的对象占用内存很大,则直接分配到老年代)。当Eden 区内存不够的时候就会触发MinorGC,对新生代区进行一次垃圾回收。
ServivorFrom区:上一次GC 的幸存者,作为这一次GC 的被扫描者。
ServivorTo区:保留了一次MinorGC 过程中的幸存者。
MinorGC和MajorGC分别属于新老两个区的垃圾回收机制
MinorGC
主要使用复制算法清理垃圾
- 首先把Eden和surviveFrom的内容复制到ServicorTo,年龄+1(如果有对象的年龄以及达到了老年的标准,则赋值到老年代区)#注意这句话就体现了分代收集
- 然后清空Eden和surviveFrom
- 最后交换surviveFrom和ServivorTo
MajorGC
老年代的对象比较稳定,所以MajorGC 不会频繁执行。在进行MajorGC 前一般都先进行了一次MinorGC,使得有新生代的对象晋身入老年代,导致空间不够用时才触发。当无法找到足够大的连续空间分配给新创建的较大对象时也会提前触发一次MajorGC 进行垃圾回收腾出空间。
就是纯纯的标记清理法,首先扫描一次所有老年代,标记出存活的对象,然后回收没
有标记的对象。MajorGC 的耗时比较长,因为要扫描再回收,没啥好说的