一、内存模型
1、程序计数器(线程私有)
一块较小的内存空间,是当前线程所指向的字节码的行号指示器。
2、虚拟机栈(线程私有)
是描述java方法执行的内存模型,每个方法在执行的同时都会创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每个方法从调用直至完成的过程,对应着一个栈帧在虚拟机中入栈到出栈的过程。
栈帧是用来存储数据和部分过程结果的数据结构,同时也被用来处理动态链接、方法返回值和异常分派。随方法的创建而创建,随着方法结束而销毁
3、本地方法栈(线程私有)
与虚拟机栈作用类似,区别是虚拟机栈为执行java方法服务,而本地方法栈则为Native方法服务。
4、堆(线程共享)
创建的对象和数组都保存在Java堆内存中,也是垃圾收集器进行垃圾手机的最重要的内存区域。现代VM采用分代手机算法,从GC角度还可分为新生代和老年代
5、方法区(线程共享)
即我们说的永久代,用于存储被JVM今安在的类信息、常量、静态变量、即时编译器编译后的代码等数据HotSpot VM把GC分代收集扩展至方法区,即使用Java堆的永久代来实现方法区,这样HotSpot的垃圾收集器就可以像管理java堆一样管理这部分内存,而 不必为方法区开发专门的内存管理器。
运行时常量池:是方法区的一部分。用于存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后存放到方法区的运行时常量池。
二、垃圾收集算法
1、标记清除算法(Mark-Sweep)
最基本的算法,分为两个阶段,标记和清除。标记阶段标记出所有要回收的对象,清除阶段回收被标记对象占用的空间。
问题:内存碎片化严重
2、复制算法(copying)
为了解决Mark-Sweep算法内存碎块化严重被提出,将内存划分为相等的两块,每次只使用其中一块,当一块内存存满后将存活的对象复制到另一块上,把已经使用的内存清除。
问题:内存被压缩到了原来的一半。存活对象增多,Copying算法的效率会大大降低
3、标记整理算法(Mark-Compact)
综合以上两个算法,为了避免缺陷提出。标记阶段和Mark-Sweep算法相同,笔记后不清理对象,而是将存活的对象移向内存另一端,然后清除端边界外的对象
三、JVM运行时内存
Java堆从运行角度可分为新生代(Eden区、FromSurvivor区和To Survivor区)和老年代。其中老年代占堆空间的2/3,新生代占堆空间的1/3,新生代中的Eden占新生代的8/10,from和to各占1/10
四、GC过程
新生代使用复制算法,老年代使用标记整理算法
五、如何判断垃圾
1、引用计数器法
一个对象如果没有任何与之相关的引用,即他们的引用计数都为零,则说明对象不太肯再被用到,那么这个对象就是可回收的对象.不能解决循环依赖问题。
2、可达性分析
为了解决引用计数器法的循环引用问题,Java使用了可达性分析的方法·。通过一些列GC roots对象作为起点搜索。如果再”GCroot“和一个对象之间没有可达路径,则称为该对象是不可达的。
注意:不可达对象不等于可回收对象,不可达对象变为可回收对象至少要经过两次标记过程。两次标记后任然是可回收对象,则面临回收。
六、Java中的引用
1、强引用
最常见,把一个对象赋值给另一个引用变量。此时它处于可达状态,不能被垃圾回收机制回收,即使该对象以后永远不会被使用也不会被JVM回收,因此强引用时造成Java内存泄漏的主要原因之一。
2、软引用
软引用需要使用SoftReference类来实现,对于只有软引用的对象来说,当系统内存足够时它不会被回收,当系统内存空间不足时被回收,软引用通常在对内存敏感的程序中。
3、弱引用
需要使用WeakReference类来实现,比软引用的生存期更短,对于只有弱引用的对象来说,只要垃圾回收机制运行,不管JVM是否有足够的内存空间,总会回收该对象占用的内存
4、虚引用
需要使用PhantomReference类来实现,不能单独使用,必须和引用列队联合使用。主要作用时跟踪对象被垃圾回收的状态
七、垃圾收集器
1、Serial垃圾收集器(单线程,复制算法)
2、ParNew垃圾收集器(Serial+多线程)
3、Parallel Scavenge 收集器(多线程复制算法、高效)
4、Serial Old收集器(单线程标记整理算法)
5、Parallel Old收集器(多线程标记整理算法)
6、CMS收集器(多线程标记清除算法)
7、G1收集器
八、JVM类加载机制
1、加载
在内存中生成一个代表这个类的java.lang.class对象,作为方法去这个类的各种数据入口
2、验证
确保Class文件的字节流中包含的信息是否符合当前虚拟机的要求,保证不会危害虚拟机
3、准备
在方法区中分配变量的内存空间
4、解析
虚拟机将常量池中的符号引用替换为直接应用的过程
5、初始化
开始执行类中定义的Java程序代码
九、类加载器
1、启动类加载器(Bootstrap ClassLoader)
负责加载%JAVA_HOME%/lib目录中,或通过-Xbootclasspath参数指定路径中的,且被虚拟机认可的类
2、扩展类加载器(Extension ClassLoader)
负责加载 %JAVA_HOME%jre/lib/ext路径下的jar包,或通过java.ext.dirs系统变指定路径中的类库
3、应用程序类加载器(Application ClassLoader)
负责加载用户路径classpath上的类库。
4、自定义类加载器(UserClassLoader)
通过继承java.lang.ClassLoader实现自定义的类加载器。
十、双亲委派模型
1、当一个类收到了类加载请求,他首先不会尝试自己去加载这个类,而是把这个请求委派给父类去完成,每层都是如此,因此所有请求都会被传宋到启动类加载。只有当父类加载器反馈自己无法完成这个请求时,子类加载器才会尝试自己去加载。
2、不论那个类加载器加载这个类,最终都会委托给最顶层的启动类加载器,这样保证了使用不同的类加载器最终得到的都是同样一个Object对象。