一、Java内存的构成
先贴原图
如图可知:整块区域分为Young Generation、Tenured Generation(年轻代→老年代 )(j在于JDK8中把存放元数据中的永久内存从堆内存中移到了本地内存(native memory)中)
咱们看下Young区:
Young区又分为:Eden、Survivor Space。
Survivor Space又分为 To Survivor、 From Survivor,如下图所示:
Eden和Survivor的比例是8:1:1
Survivor Space存在的意义:
首先说如果没有Survivor区会出现什么情况:此时每触发一次Minor GC,就会把Eden区的对象复制到老年代,这样当老年代满了之后会触发Major Gc(通常伴随着MinorGC,可以看做Full GC),比较耗时。
如果只有1个Survivor区,那当Eden区满了之后,就会复制对象到Survivor区,容易产生内存碎片化。严重影响性能。
所以使用2个Survivor区,始终保持有一个空的Survivor区,可以避免内存碎片化。
Java内存分为 堆内存(heap)和 Permanent区。
1、Java堆内存(heap):
--是 JVM 用于分配 Java对象的内存,包含活动对象和不可用对象
--堆大小通常是在服务器启动时使用 java命令中的 –Xms(最小) –Xmx(最大)标志来定义。
2、Permanent区:
--指内存的永久保存区域
--是Sun JDK和HPJDK用来加载类(class)和Meta信息的专门的内存区
--这个区域不归属Java堆内存(heap)范围
--Class在被Loader时就会被放到此,如果Java应用很大,例如类(class)很多,那么建议增大这个区域的大小来满足加载这些类的内存需求
--通过–XX:PermSize=***M–XX:MaxPermSize=***M调整
这里还有一个本地内存的概念:
·本地内存(native memory):
--是 JVM用于其内部操作的本地内存(非Java内存)
--JNI 代码和第三方本地模块(例如,本地JDBC 驱动程序)也使用本地内存
--最大本地内存大小取决于以下因素:操作系统进程内存大小限制、已经指定用于 Java 堆的内存
也就是说,整个物理机的内存可以说由以下部分构成:
物理内存 = Java 内存 + 本地内存 + 操作系统保留的内存
二、垃圾回收(Garbage Collection,GC)
1、为什么要垃圾回收
--JVM自动检测和释放不再使用的内存。
--Java 运行时JVM会执行GC,这样程序员不再需要显式释放对象。
2、垃圾回收(GC)的分类
--Minor GC
--Full GC
3、垃圾回收(GC)的产生过程
新生成的对象在Eden区完成内存分配
- 当Eden区满了,再创建对象,会因为申请不到空间,触发minor GC,进行young(eden+From survivor)区的垃圾回收。(为什么是eden+From survivor:两个survivor中始终有一个survivor是空的,空的那个被标记成To Survivor)
- minorGC时,Eden不能被回收的对象被 复制 到空的survivor(也就是放到ToSurvivor,同时Eden肯定会被清空),另一个survivor(From Survivor)里不能被GC回收的对象也会被 复制 到这个survivor(To Survivor),始终保证一个survivor是空的。(MinorGC完成之后,To Survivor 和 From Survivor的标记互换)
- 当做第3步的时候,如果发现存放对象的那个survivor满了,则这些对象被copy到old区,或者survivor区没有满,但是有些对象已经足够Old(通过XX:MaxTenuringThreshold参数来设置)默认是 15 岁,这些对象就会成为老年代。
但这也不是一定的,对于一些较大的对象 ( 即需要分配一块较大的连续内存空间 ) 则是直接进入到老年代。 - 当Old区被放满的之后,进行完整的垃圾回收,即 Full GC
- FullGC时,整理的是Old Generation里的对象,把存活的对象放入到Permanent Generation里。
4.好了看下Minor GC、Major GC和Full GC之间的区别:
1)Minor GC:年轻代的回收过程称为 Minor GC
- 当 JVM 无法为一个新的对象分配空间时会触发 Minor GC,比如当 Eden 区满了。所以分配率越高,越频繁执行 Minor GC。
- 内存池被填满的时候,其中的内容全部会被复制,指针会从0开始跟踪空闲内存。Eden 和 Survivor 区进行了标记和复制操作,取代了经典的标记、扫描、压缩、清理操作。所以 Eden 和 Survivor 区不存在内存碎片。写指针总是停留在所使用内存池的顶部。
- 执行 Minor GC 操作时,不会影响到永久代。从永久代到年轻代的引用被当成 GC roots,从年轻代到永久代的引用在标记阶段被直接忽略掉。
- 质疑常规的认知,所有的 Minor GC 都会触发“全世界的暂停(stop-the-world)”,停止应用程序的线程。对于大部分应用程序,停顿导致的延迟都是可以忽略不计的。其中的真相就 是,大部分 Eden 区中的对象都能被认为是垃圾,永远也不会被复制到 Survivor 区或者老年代空间。如果正好相反,Eden 区大部分新生对象不符合 GC 条件,Minor GC 执行时暂停的时间将会长很多
2)Major GC vs Full GC
- Major GC 是清理老年代。
- Full GC 是清理整个堆空间—包括年轻代和老年代。
- 首先,许多 Major GC 是由 Minor GC 触发的,所以很多情况下将这两种 GC 分离是不太可能的。另一方面,许多现代垃圾收集机制会清理部分永久代空间,所以使用“cleaning”一词只是部分正确。
5、垃圾回收的回收器
--串行(–XX:+UseSerialGC )
Out ofBox算法,年轻代串行复制,年老代串行标记整理,主要用于桌面应用
--并行(–XX:+UseParallelGC )
年轻代暂停应用程序,多个垃圾收集线程并行的复制收集,年老代暂停应用程序,与串行收集器一样,单垃圾收集线程标记整理。JDK6.0启用该算法后,默认启用了-XX:+UseParallelOldGC,性能大为提高
--并发(Concurrent Low Pause Collector)(–XX:+UseConcMarkSweepGC )
启用该参数,默认启用了-XX:+UseParNewGC;简单的说,并发是指用户线程与垃圾收集线程并发,程序在继续运行,而垃圾收集程序运行于其他CPU上。
---------第一次写 比较乱~有不对的还望指正