Java自动内存管理

Java自动内存管理:给对象分配内存+回收分配给对象的内存

给对象分配内存

1运行时数据区

Java虚拟机所管理的内存包括以下几个运行时数据区:
1.程序计数器(线程私有)
当前线程所执行的字节码的行号指示器,通过改变这个计数器的值来选取下一条需要执行的字节码指令。如果线程正在执行的是一个Java方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址。

2.Java虚拟机栈(线程私有)
描述了Java方法执行的内存模型:每个方法在执行的同时创建一个栈帧,用于存储局部变量表(基本类型,对象引用…)、方法出口等信息。每一个方法从调用到执行完成的过程,对应着一个栈帧在虚拟机栈中入栈到出栈的过程。
可以抛出StackOverflowError和OutOfMemoryError异常。

3.本地方法栈(线程私有)
与Java虚拟机栈的作用相似,区别是Java虚拟机栈为Java方法服务,本地方法栈为Naive方法服务。

简单地讲,一个Native Method就是一个java调用非java代码的接口。一个Native Method是这样一个java的方法:该方法的实现由非java语言实现,比如C。这个特征并非java所特有,很多其它的编程语言都有这一机制,比如在C++中,你可以用extern “C”告知C++编译器去调用一个C的函数。 “A native method is a Java method whose implementation is provided by non-java code.” 在定义一个native method时,并不提供实现体(有些像定义一个java interface),因为其实现体是由非java语言在外面实现的。

可以抛出StackOverflowError和OutOfMemoryError异常。

4.Java堆(线程共享)
所有的对象实例以及数组都要在堆上分配。
是垃圾收集器管理的主要区域,也被称作“GC堆”。
Java堆可以处于物理上不连续的内存空间中,只要逻辑上是连续的即可。大小可扩展(通过-Xmx/-Xms控制)。
可以抛出OutOfMemoryError异常。

5.方法区(线程共享)
用于存储已经被虚拟机加载的类信息、常量、静态变量等数据,也被称为“永久代”。
可以抛出OutOfMemoryError异常。
运行时常量池是方法区的一部分,用于存放编译器生成的各种字面量和符号引用。

2对象的创建与访问

对象在内存中存储的布局可以分为3块区域:对象头(header),实例数据(Instance data),对齐填充。

  • 对象头: 自身运行时数据(哈希码、GC分代年龄…) + 类型指针(确定它是哪个类的实例) + (数组长度)
  • 实例数据:各字段内容
  • 对齐填充:占位符(对象起始地址必须是8字节的整数倍)

访问定位对象的方法有两种:句柄访问(稳定,不随对象的移动而改变) + 直接指针访问(速度快)
句柄访问
直接访问

回收对象内存

1哪些内存需要回收

Java堆和方法区,只有在运行时才会知道创建哪些对象,这部分的内存分配和回收都是动态的。

2什么时候需要回收

堆中判断对象已死的方法:

  • 引用计数法:每当有一个地方引用它时,计数器值加1,引用失效时减1,计数器为0的对象是不可能被使用的。(难以解决对象相互循环引用的问题)
  • 可达性分析(引用链):通过一系列称为”GC Root”的对象作为起始点,开始向下搜索,无法到达的对象为不可用的。

引用的定义?

  • 强引用:在程序代码中普遍存在的,如Object obj = new Object()
  • 软引用:在系统将要发生内存溢出之前,会把这些对象列进回收范围进行二次回收。SoftReference
  • 弱引用:只能生存到下一次垃圾收集发生之前,无论内存是否足够下次都将把它回收。WeakReference
  • 虚引用:只是为了能在这个对象被收集器回收时受到一个系统通知。

方法区中垃圾收集内容:废弃常量(没有任何一个对象引用它)和无用的类(无实例无ClassLoader无Class对象)。

3如何回收

垃圾收集算法

  • 标记-清除算法:首先标记出所有需要回收的对象,在标记完成后统一回收。(Problems:效率不高,清除后有大量不连续内存碎片)
  • 复制算法:将可用容量划分成大小相等的两块,每次只使用一块,用完后就将还存活的对象复制到另一块上,再把已使用的这块内存空间一次清理掉。通常将内存空间分为一块较大的Eden空间和两块Survivor空间,当回收时,将Eden和Survivor中还存活的对象一次性地复制到另外一块Survivor空间上,最后清理掉Eden和用过的Survivor空间。Problems:复制算法在对象存活率高的情况下就要执行较多的复制操作,效率将会变低,所以老年代一般不选用这种算法。)
  • 标记-整理算法:首先标记出所有需要回收的对象, 然后让存活的对象向一端移动,然后直接清理掉端边界以外的内存。
  • 分代收集算法:现代虚拟机都采用“分代收集”算法。根据对象的存活周期将内存划分为几块,新生代采用“复制算法”,老年代(存活率高、没有额外空间对它分配担保)使用“标记-清除”或“标记-整理”算法回收。

垃圾回收器:
内存回收的具体实现。
1.Serial/Serial Old收集器
特性:单线程收集器,进行垃圾收集时必须暂停其他工作线程。简单高效。
使用场景:是虚拟机运行在Client模式下的默认新生代/老年代收集器。

2.Parnew收集器
特性:Serial收集器的多线程版本。
使用场景:是虚拟机运行在Server模式下的默认新生代收集器。

3.Parallel Scavenge/Parallel Old收集器
特性:多线程收集器,可控制的吞吐量。
使用场景:在注重吞吐量以及CPU资源敏感的场合,二者必须搭配使用。

4.CMS收集器(concurrent mark sweep)
特性:以获取最短回收停顿时间为目标,老年代收集器,基于“标记-清除”算法实现(空间碎片的产生)。
工作步骤:初始标记(Stop the world)->并发标记->重新标记(Stop the world)->并发清除

5.G1收集器(Garbage-First)
特性:面向服务端应用,并行与并发,分代收集,整体基于“标记-整理”算法,局部基于复制算法,可预测的停顿模型。
工作步骤:初始标记->并发标记->最终标记->筛选回收

一些内存分配原则

  1. 对象优先在新生代的Eden区中分配,当Eden区中没有足够空间进行分配时,虚拟机将发起一次Minor GC。
  2. 需要大量连续内存空间的Java大对象分配到老年代(避免多次复制)。
  3. 在Survivor区中每经过一次Minor GC,年龄就增加一次,当它的年龄增加到一定程度(可通过参数设定年龄阈值),就会被晋升到老年代中。
  4. 动态对象年龄判定:不一定非要到达阈值才能晋升老年代(当满足同年龄对象达到Survivor空间一半规则就会晋升)。

Minor GC与Full GC

Minor GC:清理年轻代的内存,会触发“Stop the world”。因为 Java 对象大多都具备朝生夕灭的特性,所以 Minor GC 非常频繁,一般回收速度也比较快。
Major GC/Full GC):发生在老年代的GC,出现了Major GC,经常会伴随至少一次的Minor GC,也可以看做进行了Full GC。(空间分配担保时会发生)老年代的内存空间远大于新生代,进行一次Full GC消耗的时间比Minor GC长得多。

其他

1.为什么需要两块Survivor区?
http://blog.csdn.net/antony9118/article/details/51425581
2.新生代晋升老年代的时机?
年龄到了 or Survivor空间不足(分配担保)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值