jvm-2-自动内存管理机制

二:自动内存管理机制

2.1 java内存区域与内存溢出异常
2.1.1 jvm运行时数据区域
  1. 程序计数器(线程私有):比较小的内存空间,作用是当前线程所执行的字节码的行号指示器,就是记录线程执行到哪里了。
  2. Java虚拟机栈(线程私有):一个线程对应一个堆栈,一个方法对应一个栈针,每个方法被执行时都会创建一个栈针,用于存储局部变量表、操作栈、动态链接、方法出口等信息,一个方法的开始和结束对应一个栈针在堆栈中的入栈和出栈,若有多个方法,则最先执行的方法对应的栈针在栈底,最后执行的方法的栈针在栈顶,对执引擎来来讲只有栈顶的栈针才是有效的,称为当前栈针,关联的方法即为当前方法。
  3. 本地方法栈:与虚拟机栈类似,只不过本地方法栈是为Native方法服务
  4. Java堆(线程共享):对象实例的分配地,收集器主要管理的区域,分为新生代和老年代,新生代又可划分为Eden区、from Survivor区 、to Survivor区,比例8:1:1
  5. 方法区(线程共享):作用,存储类信息、常量、静态变量等,也叫“永久代”,其实二者并不等价,只不过在hotspot虚拟机中使用永久代实现方法区而已,其他虚拟机并没有“永久代”概念,hotspot从java8开始已经没有了永久代,取而代之的Meataspace(元空间)。
2.2 垃圾收集器与内存分配策略
2.2.1 判断对象是否存活算法
1 引用计数法:

优点是:简单高效,在大部分情况下是一个比较不错的算法。缺点是:它无法解决对象之间的循环引用问题。引用计数法只关注计数器的值是否为0,故有此缺点。

2. 可达性算法

基本思想是:通过一系列成为"GC Root" 的对象作为起始点,从这些节点开始乡下搜索,搜索走过的路径称为 “引用链”,当一个对象到“GC Root”没有任何引用链相连时,证明此对象不可用。

GC Root:可作为GC Root的 对象有一下几种,1.虚拟机栈中的引用的对象,2.方法区中静态属性引用的对象,3.方法区中常量引用的对象,4.本地方法栈JNI中引用的对象。

通俗点讲就是,“没有删除的对象,或还存在的对象”,它为何能解决引用计数法遗留的问题?当引用指向null 时,原来的对象 已经没有任何引用了,故不可达。

2.2.2 GC(垃圾收集)算法
1.标记-清除算法(Mark-Sweep)

最基础的收集算法,其他算法都是基于此的不足进行的改进,见名知意,两步,标记和清除,缺点:1.效率低,标记和清除过程效率都不高,2.空间问题,会产生大量非连续的内存碎片,导致遇到大对象时,空间不足,又得触发GC。

2.复制算法(目前商用虚拟机新生代主流算法)

基本思路:将内存按比例划分为两块,1:1,各占50%,每次使用的时候,只使用其中一块,当这块内存使用完之后,将存活的对象复制到另一块内存中,然后把已使用过的内存空间全部清理掉。这样解决了标记-清除算法的 效率问题。

在实际使用时 按1:1 划分浪费极大,因为新生代具有“朝生夕死”的特点,IBM公司将新生代划分为Eden、from Survivor、to Survivor 三个部分,比例为8:1:1,每次使用Eden和from Survivor,回收时,将这两块区域的存活对象复制到 to Survivor 区域,然后清理掉这两块区域。这样存在 to Survivor 空间不足的问题,于是有了内存的“分配担保”,当to Survivor空间不足时,使用老年代存储。但是复制算法并不适用于老年代,因为需要复制的对象较多,且老年代没有可借助的其他内存空间,进行分配担保,故不适宜。

3.标记-整理算法(老年代收集算法)

根据老年代的特点,于是有了标记-整理算法,基本思路:标记-整理-清除,第一步和第三步与标记-清除算法一样,只不过多了一个整理过程,标记后将存活对象移到一端,然后清理掉另一端的所有对象。

2.2.3 HotSport的算法实现

HotSport虚拟机 的为了实现虚拟机的高效运行,有一些对于算法的辅助方案。

  1. 枚举根节点:例如方法区(有两部分可作为GC Root对象)现在动辄都是数百兆的大小,如果全部扫描,极大降低效率,不可饶恕,故,提出了“枚举 根节点”,就是一个提前量,在类夹杂完成之后或安全点,提前把对象引用,存起来,这样在GC 扫描时 就可直接获得这些区的结果,提升效率。使用名叫OopMap的数据结构。发生“枚举 根节点”的时间点有2处,一个是在类加载完成之后,一个是在安全点的时候。
  2. 安全点(Selfpoint) :首先,GC线程在进行GC时,所有的其他的用户线程是都要停下来的“stop the world”,单独进行GC过程,而程序执行时,并非在所有地方都可以停下俩进行GC的,只有到达安全点才能停下进行GC,也就是在安全点处引用关系不会发生变化,此处就叫安全点了。

关于停止所有用户线程进行GC,这个停止有两种方案:1.抢先式中断,2.主动式中断。现在使用的主动式中断,几乎没有虚拟机采用抢先式中断。
主动式中断:虚拟机设置一个flag,此flag在安全点处,用户线程主动轮训,true时自己执行到安全点即停止,然后开始GC。抢先式中断:发生GC时,先全部停止,发现有线程没到安全点时,此线程恢复执行,至安全点停止。过于粗暴。
3. 安全区域(SelfRegion)
安全点是,线程能够主动去轮训flag,而有种情况即当线程“不执行”时,就无法响应,这里的不执行有Sleep和Block等,,对于这种情况,“安全区”即可解决。安全区域:在一段代码片段之中,引用关系不会发生变化。也就是安全点的扩展。

2.2.4 垃圾收集器

目前虚拟机采用分代收集,及即不同的代,采用不同的收集算法和对应的收集器。

  1. Seral 收集器:新生代,“复制”,JDK 1.3之前使用的单线程收集器,简单而高效,适用于运行在Client模式下的虚拟机。
  2. Seral Old 收集器:老年代,“标记-整理”,Seral的老年代版本,单线程,Client模式下。
  3. ParNew 收集器:Seral的多线程版本。
  4. Parallel Scavenge 收集器:新生代,“复制算法”,“吞吐量优先”,“GC自适应的调节策略”。三个参数:(1)-XX:MaxGCPauseMilli(设置最大GC停顿时间),(2)-XX:GCTimeRatio(设置吞吐量是GC 停顿时间的多少倍),(3)-XX:UserAdaptiveSizePolicy(开关参数,实现自适应调节)
  5. Parall Old 收集器:老年代,“标记-整理”,“多线程”,在注重吞吐量的场合使用 Parall组合。
  6. CMS(Concurrent Mark Sweep) 收集器:“标记-清楚”,“追求停顿时间最短”,有一个参数解决“标记-清除”,带来的碎片问题。
  7. G1(Garbage First) 收集器:最前沿的收集器,没有大规模商用,之所以叫G1,是因为G1把堆分为若干大小相等的Region,后台维护一个优先列表,每次根据允许的收集时间,优先回收价值最大的Region.
2.2.5 内存分配及回收策略
  1. 对象优先在Eden区分配。
  2. 大对象直接进入老年代,故在程序中尽量避免“朝生夕死”的大对象。
  3. 长期存活的对象将进入老年代,每个对象都有一个Age计数器,到达指定阀值就进入老年代。
  4. 动态对象年龄判定,并不是永远要求对象年龄到达指定阀值就进入老年代,如果在Survivor空间中相同年龄所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象就可以直接进入老年代,无需到阀值。
  5. 空间分配担保,因为 toSurvivor空间较小,老年代作为toSurvivor的担保空间。
2.3 虚拟机性能监控与故障处理工具
2.3.1 JDK 提供的命令行工具

jps:查看虚拟机进程, jstat:虚拟机统计信息监视工具,jinfo:显示虚拟机配置参数,jmap:生成虚拟机内存转储快照,jstack:显示虚拟机的线程快照。

2.3.2 JDK的可视化工具
  1. bin目录下的jconsole(java监视与管理控制台)
  2. bin目录下的VisualVM(多合一故障处理工具):此工具功能齐全
2.4 调优案例分析与实战
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值