1.JVM堆的基本结构。
Java堆分为年轻代(Young Generation)和老年代(Old Generation);年轻代又分为伊甸园(Eden)和幸存区(Survivor区);幸存区又分为From Survivor空间和 To Survivor空间。
2.Java内存结构(堆结构,新生代[S0/S1/Elden],年老代,持久代)
创建的对象,首先放入Eden和Survivor1(可能是短时间)的当Eden满了会启动mi
nor gc,回收新生代中不再使用的对象,还要用的就放到Survivor2中移完之后eden和Survivor1中剩下的就是不再使用的对象,就将他们清理掉。Survivor1和Survivor2交换角色。
那就是原来的Survivor1成了备用的了,也就是原来的Survivor2多次在Survivor区没有被清理掉的,说明它是长时间使用的,那么将它移动到老年代。
由于对象越New越多,mi
nor时发生备用的Survivor区满了,new的对象直接放入老年代,很可能
快速的给老年代占满,白白的浪费老年代的空间,就会触发Full GC,回收老年代的对象。(年轻代存储“新生对象”,我们新创建的对象存储在年轻代中。当年轻内存占满后,会触发Mi
nor GC,清理年轻代内存空间。)
老年代存储长期存活的对象和大对象。年轻代中存储的对象,经过多次GC后仍然存活的对象会移动到老年代中进行存储。老年代空间占满后,会触发Full GC。
注:Full GC是清理整个堆空间,
包括年轻代和老年代。如果Full GC之后,堆中仍然无法存储对象,就会抛出OutOfMemoryError异常。
3.JVM的垃圾算法有哪几种?CMS收集算法的流程?
引用计数法:堆中每个对象实例都有
一个引用计数。当
一个对象被创建时,就将该对象实例分配给
一个变量,该变量计数设置为1。当任何其它变量被赋值为这个对象的引用时,计数加1(a = b,则b引用的对象实例的计数器+1),引用结束时,对象实例的引用计数器减1。任何引用计数器为0的对象实例可以被当作
垃圾收集。当
一个对象实例被
垃圾收集时,它引用的任何对象实例的引用计数器减1。缺点:无法检测出循环引用。
可达性分析算法:可达性分析算法是从离散数学中的图论引入的,程序把所有的引用关系看作一张图,从
一个节点GC ROOT开始,寻找对应的引用节点,找到这个节点以后,继续寻找这个节点的引用节点,当所有的引用节点寻找完毕之后,剩余的节点则被认为是没有被引用到的节点,即无用的节点,无用的节点将会被判定为是可回收的对象。
在Java语言中,可作为GC Roots的对象
包括下面几种:
a) 虚拟机栈中引用的对象(栈帧中的本地变量表);
b)
方法区中类静态
属性引用的对象;
c)
方法区中常量引用的对象;
d) 本地
方法栈中JNI(Native
方法)引用的对象。
复制(copying):将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块,然后把已使用的内存空间一次清理掉,当存活的对象较少时,复制算法会比较高效(新生代的Eden区就是采用这种算法),其带来的成本是需要一块额外的空闲空间和对象的移动。
标记-清除(Mark-Sweep) :
标记-清除算法分为两个阶段:
标记阶段和清除阶段。
标记阶段的任务是
标记出所有需要被回收的对象,清除阶段就是回收被
标记的对象所占用的空间。
标记-清除算法采用从根集合(GC Roots)进行扫描,对存活的对象进行
标记,
标记完毕后,再扫描整个空间中未被
标记的对象,进行回收。
标记-清除动作不需要移动对象,且仅对不存活的对象进行清理,在空间中存活对象较多的时候,效率较高,但由于只是清除,没有重新整理,因此会造成内存碎片。
标记-整理算法(Mark-compact):
标记出所有需要被回收的对象,完成
标记之后,它不是直接清理可回收对象,而是将存活对象都向一端移动,然后清理掉端边界以外的内存,因此成本更高,但是却
解决了内存碎片的问题。
分代收集算法 Generational Collection(分代收集)算法 :将堆区划分为老年代和新生代,在堆区之外还有
一个代就是永久代。
老年代的特点是每次
垃圾收集时只有少量对象需要被回收,而新生代的特点是每次
垃圾回收时都有大量的对象需要被回收。
新生代都采取
copying算法,因为新生代中每次
垃圾回收都要回收大部分对象,也就是说需要复制的操作
次数较少,但是实际中并不是按照1:1的比例来划分新生代的空
间的,一般来说是将新生代划分为一块较大的Eden空间和两块较小的Survivor空间(一般为8:1:1),每次使用Eden空间和其中的一块Survivor空间,当进行回收时,将Eden和Survivor中还存活的对象复制到另一块Survivor空间中,然后清理掉Eden和刚才使用过的Survivor空间。
而由于老年代的特点是每次回收都只回收少量对象,一般使用的是
标记-整理算法。
数据会首先分配到Eden区 当中(当然也有特殊情况,如果是大对象那么会直接放入到老年代(大对象是指需要大量连续内存空
间的java对象)),当Eden没有足够空
间的时候就会 触发jvm发起一次Mi
nor GC。如果对象经过一次Mi
nor GC还存活,并且又能被Survivor空间接受,那么将被移动到Survivor空 间当中。并将其年龄设为1,对象在Survivor每熬过一次Mi
nor GC,年龄就加1,当年龄达到一定的程度(
默认为15)时,就会被晋升到老年代 中了,当然晋升老年代的年龄是可以设置的。老年代满了就执行:Full GC 因为不经常执行,因此采用
标记-整理算法清理.
4. JVM内存结构,为什么需要GC?
JVM主要
包括四个部分:
1.类加载器(ClassLoader):在JVM启动时或者在类运行时将需要的class加载到JVM中。
2.执行引擎:负责执行class
文件中包含的字节码指令;
3.内存区(也叫运行时数据区):是在JVM运行的时候操作所分配的内存区。运行时内存区主要可以划分为5个区域:
-
-
- 方法区(Method Area):用于存储类结构信息的地方,包括常量池、静态变量、构造函数等。。
- java堆(Heap):存储java实例或者对象的地方。这块是GC的主要区域。从存储的内容我们可以很容易知道,方法区和堆是被所有java线程共享的。
- java栈(Stack):java栈总是和线程关联在一起,每当创建一个线程时,JVM就会为这个线程创建一个对应的java栈。在这个java栈中又会包含多个栈帧,每运行一个方法就创建一个栈帧,用于存储局部变量表、操作栈、方法返回值等。每一个方法从调用直至执行完成的过程,就对应一个栈帧在java栈中入栈到出栈的过程。所以java栈是现成私有的。(栈帧:每个方法在执行时都会创建一个栈帧,随着方法的调用而创建,随着方法的结束而消亡。栈帧中存储了局部变量表、操作数栈、动态连接和方法出口等信息。每个方法从调用到运行结束的过程,就对应着一个栈帧在栈中压栈到出栈的过程)。https://blog.csdn.net/qq_32258777/article/details/81353638
- 程序计数器(PC Register):用于保存当前线程执行的内存地址。由于JVM程序是多线程执行的(线程轮流切换),所以为了保证线程切换回来后,还能恢复到原先状态,就需要一个独立的计数器,记录之前中断的地方,可见程序计数器也是线程私有的。
- 本地方法栈(Native Method Stack):和java栈的作用差不多,只不过是为JVM使用到的native方法服务的。
4.本地
方法接口:主要是
调用C或C++实现的本地
方法及返回结果。
5.内存分配
java一般内存申请有两种:静态内存和动态内存。编译时就能够确定的内存就是静态内存,即内存是固定的,系统一次性分配,比如int类型变量;动态内存分配就是在程序执行时才知道要分配的存储空间大小,比如java对象的内存空间。
有3个是不需要进行
垃圾回收的:本地
方法栈、程序计数器、虚拟机栈。因为他们的生命周期是和线程同步的,随着线程的销毁,他们占用的内存会
自动释放。所以,只有
方法区和堆区需要进行
垃圾回收,回收的对象就是那些不存在任何引用的对象。
6.CMS算法的过程,CMS回收过程中JVM是否需要暂停
老年代使用CMS
垃圾回收器,需要
添加虚拟机参数-"XX:+UseCon
cmarkSweepGC"。
CMS收集器是一种基于"
标记-清除"算法实现的收集器,整个过程分为四步
1.初始
标记(CMS initial mark),需要暂停所有的工作线程.该过程分为两步:
-
- 标记GC Roots可达的老年代对象
- 遍历新生代对象,标记可达的老年代对象
2.并发
标记(CMS concurrent mark)
3.重新
标记 (CMS remark)
4.并发清除 (CMS concurrent sweep)
初始
标记、重新
标记着两个步骤任然需要"Stop The World",初始
标记仅仅只是
标记一下GC Roots能直接关联到的对象,速度很快,并发
标记就是进行GC Roots Tracing 的过程,而重新
标记阶段则是为了修正并发
标记期间因
用户程序继续运行而导致
标记产品变动的那一部分对象的
标记记录。这个阶段的停顿时间一般会比初始
标记阶段稍长一些。,但远比并发
标记的时间短。
常见的B/S架构的应用就适合这种收集器,因为其高并发、高响应的特点,CMS是基于
标记-清楚算法实现的。
CMS收集器的缺点:
1.对
cpu资源非常敏感,在并发阶段虽然不会导致
用户停顿,但是会占用
cpu资源而 导致应用程序变慢,总吞吐量下降。
2.无法处理浮动
垃圾(由于CMS并发清理阶段
用户线程还在运行着,伴随着程序的运行自然就还会有新的
垃圾不断产生,这一部分产生的
垃圾,CMS无法在当次收集中处理掉他们,只要留待下一次GC再进行处理。这一部分产生的
垃圾成为"浮动
垃圾"),可能出现“Concurrnet Mode Failure”,失败而导致另一次的Full GC。
3.CMS收集器是基于
标记-清除算法的实现,因此也会产生碎片。
一旦发生old区满了,并且浮动
垃圾未清除完的情况,就会使用Serial Old(单线程,使用的
垃圾回收算法是
标记-压缩-清理算法)进行
垃圾清除,效率极低。
7.JVM有哪些常用启动参数可以调整?
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。