JVM
内存结构(运行时数据区):堆(Heap),方法区(Method area),栈(本地方法栈(Native Method Stacks),虚拟机方法栈(Java Virtual Machine Stacks)),程序计数器(The pc Register)
堆:java虚拟机所管理的内存中最大的一块;在虚拟机启动时创建,被所有线程共享,java对象实例及数组都在堆上分配
方法区:Java虚拟机规范把方法区描述为堆的一个逻辑部分,别名Non-Heap,目的是与java堆区分开来,在java虚拟机启动时创建,被所有线程共享;用于存储已被虚拟机加载的类信息,常量,静态变量,即时编译后的代码等数据;当方法区无法满足内存要求时抛出OutOfMemoryError异常
虚拟机栈:是一个线程执行的区域,保存着一个线程中方法的调用状态;
1:一个JAVA线程的运行状态由一个虚拟机栈保存,所以虚拟机栈是线程私有的,随着线程的创建而创建
2:每一个被线程执行的方法为该栈中的栈帧,即每个方法对应一个栈帧;调用一个方法就会向栈中压入一个栈帧,一个方法调用完成,就会把该栈帧从栈中弹出(FILO先压入的栈帧后弹出)
程序计数器:线程的执行权由CPU时间分片轮换调度,当一个线程执行过程中失去了执行权时由程序计数器记录当前线程执行的位置,待线程再次获得执行权时由程序计数器记录的位置处开始执行
1:如果线程正在执行的是JAVA方法,则计数器记录的是正在执行的虚拟机字节码指令的地址
2:如果线程正在执行的是native方法,则这个计数器为空
本地方法栈:如果线程执行的是native方法,这些方法就会在本地方法栈中执行;如果在JAVA方法中调用native方法,则以动态链接的方式执行调用
内存结构(概念):老年代(old),新生代(Eden,survive0,survice1)
垃圾回收(GC):
Eden:1:在JAVA对象的头部(markWord)中会存储当前的对象GC次数,当一个对象GC次数大于18时会将此对象从新生代转移到老年代
2:当有较大的对象进入Eden区时,首先会将Eden存在的不连续空间的对象转移到survive0区;当survive0区中的对象总内存超过survive0区的一半时,将survive0区中对象复制到survive1区中以便在survive0区中空出连续的完整空间
3:当占有超过Eden区内存一半的对象时,会直接存放到老年代
什么时候会进行垃圾回收:
1:当Eden区或S区内存不够用了
2:老年代空间不够用了
3:方法区空间不够用了
4:System.gc()
垃圾回收算法:标记-清除,标记复制
可达性分析算法:通过GC Root对象(能作为GC Root:类加载器,Thread,虚拟机栈的本地变量表,static成员,常量引用,本地方法栈的变量等)开始向下寻找,看某个对象是否可达,不可达的对象标记回收
标记-清除(Mark Sweep)
标记:找出内存中需要回收的对象,并且把它们标记出来。此时堆中的所有对象都要被扫描一遍,才能确定需要被回收的对象,比较耗时。
清除: 清除掉被标记需要回收的对象,释放出对应的内存空间。
缺点: 标记清除之后会产生大量不连续的内存碎片,空间碎片太多可能会导致以后在程序运行过程中需要分配较大对象时,无法找到足够的连续内存而不得不提前触发另一次垃圾收集动作。
标记-复制(Mark Copying)
将内存划分为两块相等的区域,每次只使用其中一块,当其中一块使用完了,就将还存活的对象复制到另外一块内存上,然后把已使用过的内存空间一次清除掉。
缺点:空间利用率降低
标记-整理(Mark Compact)
标记过程与“标记清除算法”一致,但后续步骤不是直接对可回收对象进行清理,而是让存活对象都向一端移动,然后直接清理掉端边界以外的内存。 相较于“标记复制算法”来说少了一个“保留区”。
Young区:复制算法(对象在被分配之后,可能生命周期比较短,Young区复制率较高)
Old区:标记清除或整理(Old区对象存活时间比较长,复制来复制去没有必要,不如做个标记再整理)
垃圾收集器分类:
串行收集器:Serial、Serial Old
只能有一个垃圾回收线程执行,用户线程暂停;适用于内存比较小的嵌入式设备
并行收集器(吞吐量优先):Parallel Scanvenge、 Parallel Old
多条垃圾回收线程并行工作,但此时用户线程仍然处理等待状态;适用于科学计算,后台处理等交互场景
并发收集器(停顿时间优先):CMS、G!
用户线程和垃圾收集线程同时执行(可能是交替执行),垃圾收集线程在执行的时候不会停顿用户线程的运行;适用于相对时间有要求的场景,比如Web
JVM查看参数
java -XX:+PrintFlagsFinal -version > flags.txt
常用参数含义