JVM初探- 内存分配、GC原理与垃圾收集器

标签: geek | 发表时间:2016-12-31 08:00 | 作者:
出处:http://itindex.net/admin/pagedetail

JVM初探- 内存分配、GC原理与垃圾收集器

标签 : JVM


JVM内存的分配与回收大致可分为如下4个步骤: 何时分配 -> 怎样分配 -> 何时回收 -> 怎样回收.
除了在概念上可简单认为 new时分配外, 我们着重介绍后面的3个步骤:


I. 怎样分配- JVM内存分配策略

对象内存主要分配在新生代 Eden区, 如果启用了本地线程分配缓冲, 则 优先在TLAB上分配, 少数情况能会直接分配在老年代, 或被拆分成标量类型在栈上分配(JIT优化). 分配的规则并不是百分百固定, 细节主要取决于垃圾收集器组合, 以及VM内存相关的参数.


对象分配

  • 优先在Eden区分配
    JVM内存模型一文中, 我们大致了解了VM年轻代堆内存可以划分为一块Eden区和两块Survivor区. 在大多数情况下, 对象在新生代Eden区中分配, 当Eden区没有足够空间分配时, VM发起一次Minor GC, 将Eden区和其中一块Survivor区内尚存活的对象放入另一块Survivor区域, 如果在Minor GC期间发现新生代存活对象无法放入空闲的Survivor区, 则会通过 空间分配担保机制使对象提前进入老年代(空间分配担保见下).
  • 大对象直接进入老年代
    Serial和ParNew两款收集器提供了 -XX:PretenureSizeThreshold的参数, 令大于该值的大对象直接在老年代分配, 这样做的目的是 避免在Eden区和Survivor区之间产生大量的内存复制(大对象一般指 需要大量连续内存的Java对象, 如很长的字符串和数组), 因此大对象容易导致 还有不少空闲内存就提前触发GC以获取足够的连续空间.

对象晋升

  • 年龄阈值
    VM为每个对象定义了一个对象年龄(Age)计数器, 对象在Eden出生如果 经第一次Minor GC后仍然存活, 且能被Survivor容纳的话, 将被移动到Survivor空间中, 并将 年龄设为1. 以后 对象在Survivor区中每熬过一次Minor GC年龄就+1. 当增加到一定程度( -XX:MaxTenuringThreshold, 默认15), 将会晋升到老年代.

  • 提前晋升: 动态年龄判定
    然而VM并不总是要求对象的年龄必须达到 MaxTenuringThreshold才能晋升老年代: 如果在Survivor空间中相同年龄所有对象大小的总和大于Survivor空间的一半, 年龄大于或等于该年龄的对象就可以直接进入老年代, 而无须等到晋升年龄.


II. 何时回收-对象生死判定

(哪些内存需要回收/何时回收)

在堆里面存放着Java世界中几乎所有的对象实例, 垃圾收集器在对堆进行回收前, 第一件事就是判断哪些对象 已死(可回收).


可达性分析算法

在主流商用语言(如Java、C#)的主流实现中, 都是通过 可达性分析算法来判定对象是否存活的: 通过一系列的称为 GC Roots的对象作为起点, 然后向下搜索; 搜索所走过的路径称为 引用链/Reference Chain, 当一个对象到 GC Roots没有任何 引用链相连时, 即该对象不可达, 也就说明此对象是不可用的, 如下图: Object5、6、7虽然互有关联, 但它们到GC Roots是不可达的, 因此也会被判定为可回收的对象:

  • 在Java, 可作为 GC Roots的对象包括:
    1. 方法区: 类静态属性引用的对象;
    2. 方法区: 常量引用的对象;
    3. 虚拟机栈(本地变量表)中引用的对象.
    4. 本地方法栈JNI(Native方法)中引用的对象。

注: 即使在 可达性分析算法中不可达的对象, VM也并不是马上对其回收, 因为要真正宣告一个对象死亡, 至少要经历两次标记过程: 第一次是在可达性分析后发现没有与GC Roots相连接的引用链, 第二次是GC对在 F-Queue执行队列中的对象进行的小规模标记(对象需要覆盖 finalize()方法且没被调用过).


III. GC原理- 垃圾收集算法

分代收集算法 VS 分区收集算法

  • 分代收集
    当前主流VM垃圾收集都采用”分代收集”(Generational Collection)算法, 这种算法会根据对象存活周期的不同将内存划分为几块, 如JVM中的 新生代老年代永久代. 这样就可以根据各年代特点分别采用最适当的GC算法:
    • 在新生代: 每次垃圾收集都能发现大批对象已死, 只有少量存活. 因此选用复制算法, 只需要付出少量存活对象的复制成本就可以完成收集.
    • 在老年代: 因为对象存活率高、没有额外空间对它进行分配担保, 就必须采用 “标记—清理”“标记—整理”算法来进行回收, 不必进行内存复制, 且直接腾出空闲内存.
  • 分区收集
    上面介绍的分代收集算法是将对象的生命周期按长短划分为两个部分, 而分区算法则将整个堆空间划分为连续的不同小区间, 每个小区间独立使用, 独立回收. 这样做的好处是 可以控制一次回收多少个小区间.
    在相同条件下, 堆空间越大, 一次GC耗时就越长, 从而产生的停顿也越长. 为了更好地控制GC产生的停顿时间, 将一块大的内存区域分割为多个小块, 根据目标停顿时间, 每次合理地回收若干个小区间(而不是整个堆), 从而减少一次GC所产生的停顿.

分代收集

新生代-复制算法

该算法的核心是 将可用内存按容量划分为大小相等的两块, 每次只用其中一块, 当这一块的内存用完, 就将还存活的对象复制到另外一块上面, 然后把已使用过的内存空间一次清理掉.

(图片来源: jvm垃圾收集算法)

这使得每次只对其中一块内存进行回收, 分配也就不用考虑内存碎片等复杂情况, 实现简单且运行高效.

现代商用VM的新生代均采用复制算法, 但由于新生代中的98%的对象都是生存周期极短的, 因此并不需完全按照1∶1的比例划分新生代空间, 而是 将新生代划分为一块较大的Eden区和两块较小的Survivor区(HotSpot默认Eden和Survivor的大小比例为8∶1), 每次只用Eden和其中一块Survivor. 当发生MinorGC时, 将Eden和Survivor中还存活着的对象一次性地拷贝到另外一块Survivor上, 最后清理掉Eden和刚才用过的Survivor的空间. 当Survivor空间不够用(不足以保存尚存活的对象)时, 需要依赖老年代进行空间分配担保机制, 这部分内存直接进入老年代.


老年代-标记清除算法

该算法分为“标记”和“清除”两个阶段: 首先标记出所有需要回收的对象(可达性分析), 在标记完成后统一清理掉所有被标记的对象.

该算法会有以下两个问题:
1. 效率问题: 标记和清除过程的效率都不高;
2. 空间问题: 标记清除后会产生大量不连续的内存碎片, 空间碎片太多可能会导致在运行过程中需要分配较大对象时无法找到足够的连续内存而不得不提前触发另一次垃圾收集.


老年代-标记整理算法

标记清除算法会产生内存碎片问题, 而复制算法需要有额外的内存担保空间, 于是针对老年代的特点, 又有了 标记整理算法. 标记整理算法的标记过程与标记清除算法相同, 但后续步骤不再对可回收对象直接清理, 而是让所有存活的对象都向一端移动,然后清理掉端边界以外的内存.


永久代-方法区回收

  • 在方法区进行垃圾回收一般”性价比”较低, 因为在方法区主要回收两部分内容: 废弃常量无用的类. 回收废弃常量与回收其他年代中的对象类似, 但要判断一个类是否无用则条件相当苛刻:
    1. 该类所有的实例都已经被回收, Java堆中不存在该类的任何实例;
    2. 该类对应的 Class对象没有在任何地方被引用(也就是在任何地方都无法通过反射访问该类的方法);
    3. 加载该类的 ClassLoader已经被回收.

但即使满足以上条件也未必一定会回收, Hotspot VM还提供了 -Xnoclassgc参数控制(关闭CLASS的垃圾回收功能). 因此在大量使用动态代理、CGLib等字节码框架的应用中一定要关闭该选项, 开启VM的类卸载功能, 以保证方法区不会溢出.


补充: 空间分配担保

在执行Minor GC前, VM会首先检查老年代是否有足够的空间存放新生代尚存活对象, 由于新生代使用 复制收集算法, 为了提升内存利用率, 只使用了其中一个 Survivor作为轮换备份, 因此当出现大量对象在Minor GC后仍然存活的情况时, 就需要老年代进行分配担保, 让Survivor无法容纳的对象直接进入老年代, 但前提是老年代需要有足够的空间容纳这些存活对象. 但存活对象的大小在实际完成GC前是无法明确知道的, 因此Minor GC前, VM会先 首先检查老年代连续空间是否大于新生代对象总大小或历次晋升的平均大小, 如果条件成立, 则进行Minor GC, 否则进行Full GC(让老年代腾出更多空间).
然而取历次晋升的对象的平均大小也是有一定风险的, 如果某次Minor GC存活后的对象突增,远远高于平均值的话,依然可能导致担保失败(Handle Promotion Failure, 老年代也无法存放这些对象了), 此时就只好在失败后重新发起一次Full GC(让老年代腾出更多空间).


IX. GC实现- 垃圾收集器

GC实现目标: 准确、高效、低停顿、空闲内存规整.


新生代

1. Serial收集器

Serial收集器是Hotspot运行在Client模式下的 默认新生代收集器, 它的特点是 只用一个CPU/一条收集线程去完成GC工作, 且在进行垃圾收集时必须暂停其他所有的工作线程(“Stop The World” -后面简称STW).

虽然是单线程收集, 但它却简单而高效, 在VM管理内存不大的情况下(收集几十M~一两百M的新生代), 停顿时间完全可以控制在几十毫秒~一百多毫秒内.


2. ParNew收集器

ParNew收集器其实是前面 Serial的多线程版本, 除 使用多条线程进行GC外, 包括Serial可用的所有控制参数、收集算法、STW、对象分配规则、回收策略等都与Serial完全一样(也是VM启用CMS收集器 -XX: +UseConcMarkSweepGC的默认新生代收集器).

由于存在线程切换的开销, ParNew在单CPU的环境中比不上Serial, 且在通过超线程技术实现的两个CPU的环境中也不能100%保证能超越Serial. 但随着可用的CPU数量的增加, 收集效率肯定也会大大增加(ParNew收集线程数与CPU的数量相同, 因此在CPU数量过大的环境中, 可用 -XX:ParallelGCThreads参数控制GC线程数).


3. Parallel Scavenge收集器

与ParNew类似, Parallel Scavenge也是 使用复制算法, 也是 并行多线程收集器. 但与其他收集器关注 尽可能缩短垃圾收集时间不同, Parallel Scavenge更关注 系统吞吐量:

系统吞吐量=运行用户代码时间(运行用户代码时间+垃圾收集时间)

停顿时间越短就越适用于用户交互的程序-良好的响应速度能提升用户的体验;而高吞吐量则适用于后台运算而不需要太多交互的任务-可以最高效率地利用CPU时间,尽快地完成程序的运算任务. Parallel Scavenge提供了如下参数设置系统吞吐量:

Parallel Scavenge参数 描述
MaxGCPauseMillis (毫秒数) 收集器将尽力保证内存回收花费的时间不超过设定值, 但如果太小将会导致GC的频率增加.
GCTimeRatio (整数: 0 < GCTimeRatio < 100) 是垃圾收集时间占总时间的比率
-XX:+UseAdaptiveSizePolicy 启用GC自适应的调节策略: 不再需要手工指定 -Xmn-XX:SurvivorRatio-XX:PretenureSizeThreshold等细节参数, VM会根据当前系统的运行情况收集性能监控信息, 动态调整这些参数以提供最合适的停顿时间或最大的吞吐量

老年代

Serial Old收集器

Serial Old是Serial收集器的老年代版本, 同样是 单线程收集器,使用 “标记-整理”算法:

  • Serial Old应用场景如下:
    • JDK 1.5之前与Parallel Scavenge收集器搭配使用;
    • 作为CMS收集器的后备预案, 在并发收集发生 Concurrent Mode Failure时启用(见下:CMS收集器).

Parallel Old收集器

Parallel Old是Parallel Scavenge收老年代版本, 使用 多线程和“标记-整理”算法, 吞吐量优先, 主要与Parallel Scavenge配合在 注重吞吐量CPU资源敏感系统内使用:


CMS收集器

CMS(Concurrent Mark Sweep)收集器是一款具有划时代意义的收集器, 一款真正意义上的并发收集器, 虽然现在已经有了理论意义上表现更好的G1收集器, 但现在主流互联网企业线上选用的仍是CMS(如Taobao、微店).
CMS是一种以 获取最短回收停顿时间为目标的收集器(CMS又称 多并发低暂停的收集器), 基于”标记-清除”算法实现, 整个GC过程分为以下4个步骤:
1. 初始标记(CMS initial mark)
2. 并发标记(CMS concurrent mark: GC Roots Tracing过程)
3. 重新标记(CMS remark)
4. 并发清除(CMS concurrent sweep: 已死象将会就地释放, 注意: 此处没有压缩)
其中两个加粗的步骤( 初始标记重新标记)仍需STW. 但初始标记仅只标记一下GC Roots能直接关联到的对象, 速度很快; 而重新标记则是为了修正并发标记期间因用户程序继续运行而导致标记产生变动的那一部分对象的标记记录, 虽然一般比初始标记阶段稍长, 但要远小于并发标记时间.

(由于整个GC过程 耗时最长的并发标记和并发清除阶段的GC线程可与用户线程一起工作, 所以总体上CMS的GC过程是与用户线程一起并发地执行的.

由于CMS收集器将整个GC过程进行了更细粒度的划分, 因此可以实现 并发收集、低停顿的优势, 但它也并非十分完美, 其存在缺点及解决策略如下:

  1. CMS默认启动的回收线程数=(CPU数目+3)4

    当CPU数>4时, GC线程最多占用不超过 25%的CPU资源, 但是当CPU数<=4时, GC线程可能就会过多的占用用户CPU资源, 从而导致应用程序变慢, 总吞吐量降低.
  2. 无法处理浮动垃圾, 可能出现 Promotion FailureConcurrent Mode Failure而导致另一次Full GC的产生: 浮动垃圾是指在CMS并发清理阶段用户线程运行而产生的新垃圾. 由于在GC阶段用户线程还需运行, 因此还需要预留足够的内存空间给用户线程使用, 导致CMS不能像其他收集器那样等到老年代几乎填满了再进行收集. 因此CMS提供了 -XX:CMSInitiatingOccupancyFraction参数来设置GC的触发百分比(以及 -XX:+UseCMSInitiatingOccupancyOnly来启用该触发百分比), 当老年代的使用空间超过该比例后CMS就会被触发(JDK 1.6之后默认92%). 但当CMS运行期间预留的内存无法满足程序需要, 就会出现上述 Promotion Failure等失败, 这时VM将启动后备预案: 临时启用Serial Old收集器来重新执行Full GC(CMS通常配合大内存使用, 一旦大内存转入串行的Serial GC, 那停顿的时间就是大家都不愿看到的了).
  3. 最后, 由于CMS采用”标记-清除”算法实现, 可能会产生大量内存碎片. 内存碎片过多可能会导致无法分配大对象而提前触发Full GC. 因此CMS提供了 -XX:+UseCMSCompactAtFullCollection开关参数, 用于在Full GC后再执行一个碎片整理过程. 但内存整理是无法并发的, 内存碎片问题虽然没有了, 但停顿时间也因此变长了, 因此CMS还提供了另外一个参数 -XX:CMSFullGCsBeforeCompaction用于设置在执行N次不进行内存整理的Full GC后, 跟着来一次带整理的(默认为0: 每次进入Full GC时都进行碎片整理).

分区收集- G1收集器

G1(Garbage-First)是一款面向服务端应用的收集器, 主要目标用于配备多颗CPU的服务器治理大内存.
- G1 is planned as the long term replacement for the Concurrent Mark-Sweep Collector (CMS).
- -XX:+UseG1GC启用G1收集器.

与其他基于分代的收集器不同, G1将整个Java堆划分为多个大小相等的独立区域(Region), 虽然还保留有新生代和老年代的概念, 但新生代和老年代不再是物理隔离的了, 它们都是一部分Region(不需要连续)的集合.

每块区域既有可能属于O区、也有可能是Y区, 因此不需要一次就对整个老年代/新生代回收. 而是 当线程并发寻找可回收的对象时, 有些区块包含可回收的对象要比其他区块多很多. 虽然在清理这些区块时G1仍然需要暂停应用线程, 但可以用相对较少的时间优先回收垃圾较多的Region(这也是G1命名的来源). 这种方式保证了G1可以在有限的时间内获取尽可能高的收集效率.


新生代收集

G1的新生代收集跟ParNew类似: 存活的对象被转移到一个/多个 Survivor Regions. 如果存活时间达到阀值, 这部分对象就会被提升到老年代.

  • G1的新生代收集特点如下:
    • 一整块堆内存被分为多个Regions.
    • 存活对象被拷贝到新的Survivor区或老年代.
    • 年轻代内存由一组不连续的heap区组成, 这种方法使得可以动态调整各代区域尺寸.
    • Young GCs会有STW事件, 进行时所有应用程序线程都会被暂停.
    • 多线程并发GC.

老年代收集

G1老年代GC会执行以下阶段:

注: 一下有些阶段也是年轻代垃圾收集的一部分.

index Phase Description
(1) 初始标记 (Initial Mark: Stop the World Event) 在G1中, 该操作附着一次年轻代GC, 以标记Survivor中有可能引用到老年代对象的Regions.
(2) 扫描根区域 (Root Region Scanning: 与应用程序并发执行) 扫描Survivor中能够引用到老年代的references. 但必须在Minor GC触发前执行完.
(3) 并发标记 (Concurrent Marking : 与应用程序并发执行) 在整个堆中查找存活对象, 但该阶段可能会被Minor GC中断.
(4) 重新标记 (Remark : Stop the World Event) 完成堆内存中存活对象的标记. 使用 snapshot-at-the-beginning(SATB, 起始快照)算法, 比CMS所用算法要快得多(空Region直接被移除并回收, 并计算所有区域的活跃度).
(5) 清理 (Cleanup : Stop the World Event and Concurrent) 见下 5-1、2、3
5-1 (Stop the world) 在含有存活对象和完全空闲的区域上进行统计
5-2 (Stop the world) 擦除Remembered Sets.
5-3 (Concurrent) 重置空regions并将他们返还给空闲列表(free list)
(*) Copying/Cleanup (Stop the World Event) 选择”活跃度”最低的区域(这些区域可以最快的完成回收). 拷贝/转移存活的对象到新的尚未使用的regions. 该阶段会被记录在gc-log内(只发生年轻代 [GC pause (young)], 与老年代一起执行则被记录为 [GC Pause (mixed)].

详细步骤可参考 Oracle官方文档-The G1 Garbage Collector Step by Step.

  • G1老年代GC特点如下:
    • 并发标记阶段(index 3)
      1. 在与应用程序并发执行的过程中会计算活跃度信息.
      2. 这些活跃度信息标识出那些regions最适合在STW期间回收( which regions will be best to reclaim during an evacuation pause).
      3. 不像CMS有清理阶段.
    • 再次标记阶段(index 4)
      1. 使用Snapshot-at-the-Beginning(SATB)算法比CMS快得多.
      2. 空region直接被回收.
    • 拷贝/清理阶段(Copying/Cleanup Phase)
      • 年轻代与老年代同时回收.
      • 老年代内存回收会基于他的活跃度信息.

补充: 关于Remembered Set

G1收集器中, Region之间的对象引用以及其他收集器中的新生代和老年代之间的对象引用都是 使用Remembered Set来避免扫描全堆. G1中每个Region都有一个与之对应的Remembered Set, VM发现程序对Reference类型数据进行写操作时, 会产生一个Write Barrier暂时中断写操作, 检查Reference引用的对象是否处于不同的Region中(在分代例子中就是检查是否老年代中的对象引用了新生代的对象), 如果是, 便通过CardTable把相关引用信息记录到被引用对象所属的Region的Remembered Set中. 当内存回收时, 在GC根节点的枚举范围加入Remembered Set即可保证不对全局堆扫描也不会有遗漏.


V. JVM小工具

在${JAVA_HOME}/bin/目录下Sun/Oracle给我们提供了一些处理应用程序性能问题、定位故障的工具, 包含

bin 描述 功能
jps 打印Hotspot VM进程 VMID、JVM参数、 main()函数参数、主类名/Jar路径
jstat 查看Hotspot VM 运行时信息 类加载、内存、GC[ 可分代查看]、JIT编译
jinfo 查看和修改虚拟机各项配置 -flag name=value
jmap heapdump: 生成VM堆转储快照、查询finalize执行队列、Java堆和永久代详细信息 jmap -dump:live,format=b,file=heap.bin [VMID]
jstack 查看VM当前时刻的线程快照: 当前VM内每一条线程正在执行的方法堆栈集合 Thread.getAllStackTraces()提供了类似的功能
javap 查看经javac之后产生的JVM字节码代码 自动解析 .class文件, 避免了去理解class文件格式以及手动解析class文件内容
jcmd 一个多功能工具, 可以用来导出堆, 查看Java进程、导出线程信息、 执行GC、查看性能相关数据等 几乎集合了jps、jstat、jinfo、jmap、jstack所有功能
jconsole 基于JMX的可视化监视、管理工具 可以查看内存、线程、类、CPU信息, 以及对JMX MBean进行管理
jvisualvm JDK中最强大运行监视和故障处理工具 可以监控内存泄露、跟踪垃圾回收、执行时内存分析、CPU分析、线程分析…

VI. VM常用参数整理

参数 描述
-Xms 最小堆大小
-Xmx 最大堆大小
-Xmn 新生代大小
-XX:PermSize 永久代大小
-XX:MaxPermSize 永久代最大大小
-XX:+PrintGC 输出GC日志
-verbose:gc -
-XX:+PrintGCDetails 输出GC的详细日志
-XX:+PrintGCTimeStamps 输出GC时间戳(以基准时间的形式)
-XX:+PrintHeapAtGC 在进行GC的前后打印出堆的信息
-Xloggc:/path/gc.log 日志文件的输出路径
-XX:+PrintGCApplicationStoppedTime 打印由GC产生的停顿时间

在此处无法列举所有的参数以及他们的应用场景, 详细移步 Oracle官方文档-Java HotSpot VM Options.


参考 & 扩展 深入理解Java虚拟机 JVM内幕:Java虚拟机详解(力荐) JVM中的G1垃圾回收器 G1垃圾收集器入门 Getting Started with the G1 Garbage Collector 深入理解G1垃圾收集器 解析JDK 7的Garbage-First收集器 The Garbage-First Garbage Collector Memory Management in the Java HotSpot Virtual Machine Java HotSpot VM Options JVM实用参数(一)JVM类型以及编译器模式 JVM内存回收理论与实现 基于OpenJDK深度定制的淘宝JVM(TaobaoVM)

相关 [jvm 内存 gc] 推荐:

JVM内存组成和GC机制

- - Mz的博客
Java 虚拟机具有一个堆,堆是运行时数据区域,所有类实例和数组的内存均从此处分配. 堆是在 Java 虚拟机启动时创建的. 在JVM中堆之外的内存称为非堆内存(Non-heap memory). 可以看出JVM主要管理两种类型的内存: 堆和 非堆. 简单来说堆就是Java代码可及的内存,是留给开发人员使用的;非堆就是JVM留给 自己用的,所有方法区、JVM内部处理或优化所需的内存(如JIT编译后的代码缓存)、每个类结构(如运行时常数池、字段和方法数据)以及方法和构造方法 的代码都在非堆内存中.

【JVM】HotSpot JVM内存管理和GC策略总结

- - ITeye博客
JVM的相关知识是学习java高级特性必须要去深入学习的. 平时也有一些学习和实践,不过总结比较少. 今天有时间总结一下最基础的内存模型和GC策略的知识,在此记录一下. hotspot jvm内存模型. hotspot的内存模型很多地方都有类似总结,我也简单总结了一下,大概可以用下图表示:. 1.线程栈:线程创建是会为每个线程创建一个线程栈,线程栈里面会为每个方法调用创建一个栈帧.

JVM初探——使用堆外内存减少Full GC

- - ImportNew
GCIH可以联想到:  将长期存活的对象(如Local Cache)移入堆外内存(off-heap, 又名. 直接内存/direct-memory), 从而减少CMS管理的对象数量, 以降低Full GC的次数和频率, 达到提高系统响应速度的目的.. 这个idea最初来源于TaobaoJVM对OpenJDK定制开发的GCIH部分(详见 撒迦的分享- JVM定制改进@淘宝), 其中GCIH就是将CMS Old Heap区的一部分划分出来, 这部分内存虽然还在堆内, 但已不被GC所管理.

JVM初探- 内存分配、GC原理与垃圾收集器

- - IT瘾-geek
JVM初探- 内存分配、GC原理与垃圾收集器. JVM内存的分配与回收大致可分为如下4个步骤: 何时分配 -> 怎样分配 -> 何时回收 -> 怎样回收. new时分配外, 我们着重介绍后面的3个步骤:. 怎样分配- JVM内存分配策略. 对象内存主要分配在新生代 Eden区, 如果启用了本地线程分配缓冲, 则 优先在TLAB上分配, 少数情况能会直接分配在老年代, 或被拆分成标量类型在栈上分配(JIT优化).

JVM垃圾回收(GC)原理

- kill - yiihsia[互联网后端技术]_yiihsia[互联网后端技术]
引用计数(Reference Counting). 原理是此对象有一个引用,即增加一个计数,删除一个引用则减少一个计数. 垃圾回收时,只用收集计数为0的对象. 此算法最致命的是无法处理循环引用的问题. 标记-清除(Mark-Sweep). 第一阶段从引用根节点开始标记所有被引用的对象,第二阶段遍历整个堆,把未标记的对象清除.

JVM的GC简介和实例

- - 搜索技术博客-淘宝
本文是一次内部分享中总结了jvm gc的分类和一些实例, 内容是introduction级别的,供初学人士参考. 成文仓促,难免有些错误,如果有大牛发现,请留言,我一定及时更正,谢谢. JVM内存布局主要包含下面几个部分:. Java Virtual Machine Stack: 也就是我们常见的局部变量栈,线程私有,保存线程执行的局部变量表、操作栈、动态连接等.

如何查看GC 及jvm配置

- - 操作系统 - ITeye博客
java虽然是自动回收内存,但是应用程序,尤其服务器程序最好根据业务情况指明内存分配限制. 表示JVM Heap(堆内存)最小尺寸128MB,初始分配. 表示JVM Heap(堆内存)最大允许的尺寸256MB,按需分配. 说明:如果-Xmx不指定或者指定偏小,应用可能会导致java.lang.OutOfMemory错误,此错误来自JVM不是Throwable的,无法用try...catch捕捉.

JVM内存分配

- - 移动开发 - ITeye博客
计算机内存,它算是CPU与计算机打交道最频繁的区域,所有数据都是先经过硬盘至内存,然后由CPU再从内存中获取数据进行处理,又将数据保存到内存,通过分页或分片技术将内存中的数据再flush至硬盘. 那JVM的内存结构到底是如何呢. JVM做为一个运行在操作系统上,但又独立于os运行的平台,它的内存至少应该包括象寄存器、堆栈等区域.

FULL GC有可能导致JVM暂停1分钟以上吗?

- - 高级语言虚拟机
作者: qianhd . 链接: http://hllvm.group.iteye.com/group/topic/28745 . 发表时间: 2011年12月30日. 声明:本文系ITeye网站发布的原创文章,未经作者书面许可,严禁任何网站转载本文,否则必将追究法律责任. 与另外个程序交互有个心跳检测, 15s/次, 31s没有收到心跳检测的返回消息就认为连接断了,.

jvm对象分配及GC代码实例

- - Java - 编程语言 - ITeye博客
虚拟机采用的是HotSpot内核.   1.对象优先分配在Eden区,如果Eden区没有足够的空间时,虚拟机执行一次Minor GC.   2.大对象直接进入老年代(大对象是指需要大量连续内存空间的对象). 这样做的目的是避免在Eden区和两个Survivor区之间发生大量的内存拷贝. 通过参数-XX:PretenureSizeThreshold=3145728控制.