昨天休息。。。真的懒得学习,今天继续,由于今天在公司所以只有jvm可看了。
先来复习一下,jvm的内存的结构和垃圾回收。
1、复习jvm内存模型
a.程序计数器
一块较小的内存空间,用来存储程序执行的字节码的行号指示器。这是唯一一个没有OutOfMemoryError的区域。(线程私有,生命周期和线程同生共死)
b、java虚拟机栈
描述java方法的执行模型,每个方法执行的时候都会创建一个栈帧用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每一个方法从调用直至执行完成的过程,对应着一个栈帧的入栈和出栈的过程。通常意义上的栈内存指的就是虚拟机栈的局部变量表,存储了编译期间可知的各种数据类型、对象引用。其中64位的long和double会占用两个局部变量空间,其余的数据类型占1个,也就是一个局部变量空间为32位。(线程私有,生命周期和线程同生共死),拥有oom异常。
c、本地方法栈
本地方法栈和虚拟机栈的作用类似,他们的区别在于一个执行的是java方法,一个执行的是native方法。拥有oom异常和sof异常。
d、java堆(gc堆)
堆一般为jvm中占用内存最大的一块,java对是所有线程公用的内存区域,在jvm启动的时候被创建。主要用来存放java对象,线程共有的java堆会划分许多私有的分配缓冲区,TLAB。
c、方法区(永久代)
方法区和堆类似,都为线程共有区域,只不过它用来加载常量,静态变量,即时编译的代码等数据。存在oom异常
e、运行时常量池
它是方法区的一部分。用来存放class文件除了有类的版本,字段,方法,接口等描述信息外,还有一项是常量池(Constant Pool Table),用于存放编译期间生产的各种字面量和符号引用,这部分类容在类加载后存入方法区的运行时常量池中存放。
f、直接内存
Direct Memory is not jvm’s data,同时也不是虚拟机规范中定义的内存区域。主要用于存储buffer对象的操作。
2、垃圾回收
- 引用计数法
有引用+1无引用-1,无法解决对象之间的循环引用问题,即使对象已经不被使用,但是由于互相引用,无法清除该对象。
- 可达性分析算法
通过GC Roots寻找存货对象,在java中可作为GC Roots的对象包括以下几种:
虚拟机栈中引用的对象
方法区中静态属性引用的对象
方法区中常量引用的对象
本地方法栈中JNI
- jvm对于引用概念的区分
强引用,类似于Objec o=new Object();只要存在就不会被回收
软引用,弱引用,虚引用。
- 生存还是死亡
当一个对象想要被回收至少要经理两次标记,如果第一次标志此对象不可达将会被判定需要执行finaliz()方法,如果该对象被一次标记,将会被存入F-QUEUE的队列之中。然后由jvm创建的低优先级的线程去回收。
方法区的回收主要是判定一个常量是否为“废弃常量”,需满足3个条件:
该类所有的实例都已经被回收,java堆不存在任何该类的实例
该类的classloader已经被回收
该类的java.lang.class没有被任何对象所引用,无法通过反射访问到该对象
- 垃圾回收算法
标记-清除:
标记出所有处于回收状态的对象,然后清除,效率较差
复制算法:
按照容量将内存分为相同的两块大小,每次只使用其中一块,当这一块使用完,将存活对象复制到另一块上面,然后将另一个半区回收。目前主流jvm的回收算法都是采用这种回收算法,但是堆内的内存大多都是朝生夕死,没有必要将空间划分为1:1。所以目前将内存划分为一块较大的Eden空间和两块较小的Survivor空间,每次使用其中Eden和其中一块Servivor空间。Eden和Servivor空间的比例一般为8:1,也就是每次新生代可使用的空间为堆的90%。
标记整理算法
将存活对象移至一端,然后清楚边界之外所有的对象。
分代收集算法
这种思想是按照象生存周期不同的特点,将堆分为新生代,老年代,新生代拥有较多的gc所以采用复制算法,老年代则采用标记整理或者标记清楚算法来回收。
- 垃圾回收器
serial收集器
serial收集器是最古老,发展历史最悠久的收集器,单线程,而且是新生代收集器的唯一选择。此处单线程的意义不仅仅是使用一个cpu,当它工作时它会暂停jvm所有的线程,进行回收。
parnew收集器
parnew是serial的多线程版本
Parallel Scavenge收集器
吞吐量=运行代码时间/(运行用户代码时间+垃圾回收时间),关注点不同,它是尽可能缩短垃圾手机时用户线程的停顿时间。
-XX:GCTimeRatio 可以用来控制吞吐量
-XX:MaxGCPsuseMills 用来控制jvm最大停顿时间
serial old收集器
serial收集器老年代版本,单线程,标志-整理。
parallel old收集器
parallel scavenge收集器的老年代版本,使用多线程和标志-整理算法。
cms收集器
cms收集器默认启动的线程数是(CXP数量+3)/4
初始标记
并发标记
重新标记
并发清除
初始标记和重新标记仍然需要stop the world,初始标记,仅仅标记gc roots能够关联到的对象,并发标记阶段就是gc roots tracing,重新标记标记因为用户程序产生变动的那部分。
cms收集器的缺点:
a、对于cpu资源敏感,默认启动的线程数是(CXP数量+3)/4,当只有两个cpu时,一半收集线程,一半工作线程,会有效率问题。
b、无法处理浮动垃圾,可能出现Concurrent Mode Failure失败导致一次Full Gc。
浮动垃圾的概念,就是这一次垃圾清除的时候,会产生新的垃圾,但是本次gc无法清除,只能等待下次gc。
c、标记清除,由于采用的是标记清除算法,所以将会产生大量的碎片空间。
-XX:CMSFullGCsBeforeCompaction 设置多少次gc后进行压缩空间
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------