Java中引用类型vs值类型&对引用类型所做的GC操作简介

值类型vs引用类型

 

从概念上区别下二者:

 

引用类型表示你操作的是同一数据,传入一个参数给另一个方法,如果在方法中修改该变量,当再次调用这个方法传入参数的时候,变量的值会改变。

值类型表示复制一个当前变量传给方法,在方法中修改变量之后,最初的变量值不会改变。

 

导致这种差异的原因在于二者在内存分配上的不同:

 

值类型,当声明一个变量的时候,编译器会在栈内存中分配一个空间,这个空间对应着这个值类型变量,空间中存贮的就是变量的值,实实在在值。

引用类型,引用类型的实例分配在堆内存中,创建一个引用类型的实例时,得到的变量值对应着是这个实例的在堆内存中的分配地址,地址。

 

内存分配方式的不同对性能有哪些影响:

 

值类型可以直接操作内存

而引用类型需要先解析引用地址,系统性能开销相应较大。(这个是从理论上来讲,实际上并没有什么区别,有时引用类型甚至比值类型更快)

 

经常使用的值类型和引用类型都有哪些

 

值类型:基本的数据类型,四类八种:

整形:byte,int,long,short

浮点型:float,double

字符:char

逻辑类型:boolean

引用类型:所有其他类型都是引用类型

数组,类,接口,字符串(String,这里特别注意String是特殊的引用类型,不要当成值类型)

 

Java中存储数据为什么要分为栈内存和堆内存

 

当一个方法执行的时候,每个方法都会创建自己的内存栈,在这个方法中定义的变量会不停的被放入这个内存栈中,随着方法的执行结束,这个内存栈也会自动销毁。因此所有方法中的变量都被定义在栈内存中。

我们在程序中创建一个对象的时候,这个对象将被放在运行数据区中,以便反复使用,毕竟对象的创建成本比较大,这个运行数据区就是堆内存,堆内存中的数据不会随着方法的结束而自动销毁,因为可能还会有其他变量引用这个对象,只有当一个对象没有任何变量引用它时,java 的系统垃圾回收机制才会在合适的时候回收它。

 

Java垃圾回收机制:GC(garbagecollection)垃圾回收器

 

功能是自动监测对象是否超过作用域从而达到自动回收内存的目的。GC从本质上来讲就是后台的一个低优先级进程,但是可以根据内存的使用情况动态的调整它的优先级。

对于程序员来说,创建一个对象要使用new关键字,释放对象时,只要将对象的所有引用置为null,让程序不能够再访问到这个对象,这时我们称这个对象为“不可达”,GC就是负责回收所有“不可达”的对象的内存空间。

对于GC来说,从程序员创建对象开始,它就一直关注着对象的地址,大小和使用情况。GC采用有向图的方式记录和管理堆中的所有对象,通过这种方式确定哪些对象是“可达”的,哪些是“不可达”的,当GC确定一些对象是“不可达”时,GC就有责任回收这些内存空间。

 

GC回收的原则是—“分代回收”,基于不同的对象的生命周期不同,分代回收可以提高垃圾回收效率。

 

分代具体分为年轻代(Young Generation),年老代(Old Generation)和持久代(Permanent Generation)。

 

分别说一下,

年轻代:对象在创建的时候,内存的分配首先发生在年轻代,大部分对象(经统计是98%的对象)在创建后很快会不再使用,因此很快变得“不可达”,所以年轻代主要目标是尽快消除生命周期短的对象。

年轻代分为Eden区(由伊甸园而来)和Survivor区(分为survivor0和suvivor1),其中对象的创建都发生的Eden区,Eden区满,促发一次ScavengeGC,将其中存活的对象复制到任意一个survivor中,这里假设是survivor0中,当survivor0也装满时,再将其中存活的对象复制到survivor1中去,因为两个survivor分区地位是平等对称的,所以此时survivor中可能既有Eden区直接复制过来的存活的对象,也有survivor0中复制过来的存活的对象。

来分析下这个过程,年轻代中Eden区是连续的空间,当有新对象生成并且在Eden区申请空间失败时,就会触发Scavenge GC/Minor GC, 由于GC会频繁进行,Eden区一般不会分配的很大,这时进行的GC也不会影响到年老代; survivor中总有一个为空,经过一次GC和复制,一个survivor区中保存着当前还活着的对象,而Eden和另一个survivor区中的内容不再需要了可以直接清空,到下一次GC时,两个survivor的角色互换,这种垃圾回收方式叫做“停止----复制”清理法。这种了清理法的优点在于不会出现碎片,缺点也比较明显:一个是需要暂停整个应用,另外需要两倍的内存空间。

 

年老代:当对象在两个survivor区中经历了N次(hotspot系统中是16次)垃圾回收之后仍然存活,就会被放到年老代,年老代中存放的都是一些生命周期较长的对象。

Full GC是对整个堆进行整理,比ScavengeGC要慢,所以我们要尽量减少Full GC的次数,在JVM调优的过程中很大一部分工作就是对Full GC的调节,那么会导致Full GC的原因有:

1 年老代被写满

2 持久代被写满

3  System.gc()被调用

4 上一次GC之后Heap的各域分配策略有动态变化。

 

持久代:存放class信息和方法信息。

 

GC除了上文提到的“停止—复制”清理法,还有引用计数法,标记—清扫法。

引用计数:每个对象都含有一个引用计数器,当连接到对象的时候计数器就加1,当离开作用域或者被置为null的时候引用计数器就减1。垃圾回收的时候释放计数器为0的内存空间。

简单但速度很慢,而且不能处理循环引用,会出现“对象应该被回收但计数器不为0”的情况。


停止—复制:先暂停程序的运行,将所有存活的对象从一个堆复制到另一个堆。没有被复制的都是垃圾,可以清除。

优点是没有碎片,缺点是需要暂停整个程序而且需要两倍的内存空间。


标记—清扫法:先暂停程序的运行,遍历所有引用,没找到一个存活对象就给他一个标记,此过程不进行回收。当标记结束后才开始清理。清理后,剩下的部分是不连续的。如果希望得到联系的空间则需要进行复制。

速度较快,占用空间少,但是释放后空间不连续。


GC垃圾回收不仅在java中使用,所以它的方法和触发时机都很多种,如上,那么java虚拟机中是采用哪种的呢,java的做法很简单,我们称之为“自适应”的垃圾回收器,或者是“自适应的、分代的、停止-复制、标记-清扫”式垃圾回收器。它会根据不同的环境和需要选择不同的处理方式。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值