java内存结构和垃圾回收机制

java和c语言不同

开发c语言项目,需要程序员手动清理垃圾,而java有垃圾自动回收机制。它可以减少内存异常发生的几率。

一、java的内存结构,可以分为五个区域:

1、程序计数器,它保存的是程序当前执行的指令的地址,也就是说是用来指示 执行哪条指令的。在多线程的环境下,每个线程都要有自己的程序计数器,这样在线程切换的时候,每个线程能恢复到线程之前的执行位置。

2、java栈,Java栈中存放的是一个个的栈帧,每个栈帧对应一个被调用的方法,在栈帧中包括局部变量表、操作数栈(Operand Stack)、常量池、方法返回地址和一些额外的附加信息。当线程执行一个方法时,就会创建一个对应的栈帧,执行开始叫入栈,执行结束叫出栈。对于所有的程序设计语言来说,栈这部分空间对程序员来说是不透明的,这部分空间的分配和释放都是由系统自动实施的。由于每个线程正在执行的方法可能不同,因此每个线程都会有一个自己的Java栈,互不干扰。

3、本地方法栈(native),它和java栈很相似,只不过,java栈是为java方法服务的,而本地方法栈是为本地方法(非java语言编写的方法)服务的。

4.、堆,是内存回收的主要区域,用来存储对象以及数组。在C语言中,程序员通过malloc函数和free函数在堆上申请和释放空间。在java中,程序员基本不用去关心空间释放的问题,Java的垃圾回收机制会自动进行处理。堆是被所有线程共享的,在JVM中只有一个堆。

5、方法区,用于存储已经被虚拟机加载的类信息(即加载类时需要加载的信息,包括版本、field、方法、接口等信息)、final常量、静态变量、编译器即时编译的代码等。方法区和堆一样是线程共享的区域。方法区主要存储一下信息:

---这个类型的完整有效名称(全名=包名.类名)
---这个类型直接父类的完整有效名称( java.lang.Object除外,其他类型若没有声明父类,默认父类是Object)
---这个类型的修饰符(public、abstract、final的某个子集)
---这个类型直接接口的一个有序列表 
---除此之外还方法区(Method Area)存储类信息还有
---类型的常量池( constant pool)
---域(Field)信息
---方法(Method)信息
---除了常量外的所有静态(static)变量

二、java垃圾回收算法

1、标记-清理,首先标记出所有需要回收的对象,在标记完成之后统一回收掉所有被标记的对象。简而言之,找没有用的对象,然后清理。标记-清除算法的缺点有两个:首先,效率问题,标记和清除效率都不高。其次,标记清除之后会产生大量的不连续的内存碎片,空间碎片太多会导致当程序需要为较大对象分配内存时无法找到足够的连续内存而不得不提前触发另一次垃圾收集动作。所以这个方法会导致效率和空间问题。

2、标记-清理-整理,将可用内存按容量分成大小相等的两块,每次只使用其中一块,当这块内存使用完了,就将还存活的对象复制到另一块内存上去,然后把使用过的内存空间一次清理掉。这样使得每次都是对其中一块内存进行回收,内存分配时不用考虑内存碎片等复杂情况,只需要移动堆顶指针,按顺序分配内存即可,实现简单,运行高效。复制算法的缺点显而易见,可使用的内存降为原来一半。

3、分代回收,将内存分为新生代和年老代,新生代分为Eden区、Survivor from和Survivor to三部分。当新创建对象时,一般在新生代中分配内存空间,当新生代垃圾收集器回收几次之后仍然存活的对象会被移动到年老代内存中,当大对象在新生代中无法找到足够的连续内存时也直接在年老代中创建。生代使用复制和标记-清除垃圾收集算法。在新生代里,98%的对象是朝生夕死的短生命周期对象,所以不需要将新生代划分为容量大小相等的两部分内存,而是将新生代分为Eden区,Survivor from和Survivor to三部分,其占新生代内存容量默认比例分别为8:1:1,其中Survivor from和Survivor to总有一个区域是空白,只有Eden和其中一个Survivor总共90%的新生代容量用于为新创建的对象分配内存,只有10%的Survivor内存浪费,当新生代内存空间不足需要进行垃圾回收时,仍然存活的对象被复制到空白的Survivor内存区域中,Eden和非空白的Survivor进行标记-清理回收,两个Survivor区域是轮换的。当新生代中无足够空间为对象创建分配内存,年老代中内存回收也无法回收到足够的内存空间,并且新生代和年老代空间无法在扩展时,堆就会产生OutOfMemoryError异常。

附带idea配置vm  option参数示例详解:

-Xms768m:设置JVM初始堆内存为768m。此值可以设置与-Xmx相同,以避免每次垃圾回收完成后JVM重新分配内存。 
-Xmx768m:设置JVM最大堆内存为768m。 
-Xss128k:设置每个线程的栈大小。JDK5.0以后每个线程栈大小为1M,之前每个线程栈大小为256K。应当根据应用的线程所需内存大小进行调整。在相同物理内存下,减小这个值能生成更多的线程。但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在3000~5000左右。需要注意的是:当这个值被设置的较大(例如>2MB)时将会在很大程度上降低系统的性能。 
-Xmn2g:设置年轻代大小为2G。在整个堆内存大小确定的情况下,增大年轻代将会减小年老代,反之亦然。此值关系到JVM垃圾回收,对系统性能影响较大,官方推荐配置为整个堆大小的3/8。 
-XX:NewSize=1024m:设置年轻代初始值为1024M。 
-XX:MaxNewSize=1024m:设置年轻代最大值为1024M。 
-XX:OldSize=2048m : 设置JVM启动分配的老年代内存大小.
-XX:PermSize=256m:设置持久代初始值为256M。 永久代不属于堆内存,堆内存只包含新生代和老年代
-XX:MaxPermSize=256m:设置持久代最大值为256M。 
-XX:NewRatio=4:设置年轻代(包括1个Eden和2个Survivor区)与年老代的比值。表示年轻代比年老代为1:4。 
-XX:SurvivorRatio=4:设置年轻代中Eden区与Survivor区的比值。表示2个Survivor区(JVM堆内存年轻代中默认有2个大小相等的Survivor区)与1个Eden区的比值为2:4,即1个Survivor区占整个年轻代大小的1/6。 
-XX:MaxTenuringThreshold=7:表示一个对象如果在Survivor区(救助空间)移动了7次还没有被垃圾回收就进入年老代。如果设置为0的话,则年轻代对象不经过Survivor区,直接进入年老代,对于需要大量常驻内存的应用,这样做可以提高效率。如果将此值设置为一个较大值,则年轻代对象会在Survivor区进行多次复制,这样可以增加对象在年轻代存活时间,增加对象在年轻代被垃圾回收的概率,减少Full GC的频率,这样做可以在某种程度上提高服务稳定性。
我的配置~

-Xms3072m
-Xmx3072m
-XX:NewSize=400m
-XX:PermSize=128m
-XX:MaxPermSize=256m
-XX:NewRatio=4
-XX:SurvivorRatio=4
-Dfile.encoding=UTF-8

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xinqing5130

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值