JAVA引用的种类
最近在进行Java项目开发的时候,由于业务的原因,有时候new的对象会比较多,这个时候我总是有一个疑惑?那就是JVM在何时决定回收一个Java对象所占据的内存?这个问题其实对整个web系统来说是一个比较核心的性能问题了,因为众所周知,Java也是会发生内存泄漏的。经过几天的学习和查询资料,现在先来分析一下Java的引用的种类,Java的引用就是指向Java堆内存中对象的箭头的另一端的元素。
可以先来分析一下对象在内存中的状态,对于JVM的垃圾回收机制来说,是否回收一个对象的标准在于:是否还有引用变量引用该对象?只要有引用变量引用该对象,垃圾回收机制就不会回收它。
也就是说,当Java对象被创建出来之后,垃圾回收机制会实时监控每一个对象的运行状态,包括对象的申请、引用、被引用、赋值等。当垃圾回收机制实时地监控到某个对象不再被引用变量所引用时,立即回收机制就会回收它所占的空间。
从Java内存的角度来考虑程序运行的效率,如果我们编程的时候不注意对象的合理分配,疯狂的使用new来申请对象,导致JVM内存变小,降低程序的运行效率。垃圾回收机制回收对象的时候,后台是会再起一条线程,无端的经常运行会导致程序的运行效率变慢。
我们用一段代码和UML图来查看Java对象的状态:
package com.sunyard.test;
public class NodeTest{
public static void main(String[] args) {
Node n1 = new Node("第一个节点");
Node n2 = new Node("第二个节点");
Node n3 = new Node("第三个节点");
n1.next = n2;
n2 = null;
n3 = n2;
}
}
class Node {
Node next;
String name;
public Node(String name){
this.name = name;
}
@Override
public String toString() {
return "Node [next=" + next + ", name=" + name + "]";
}
}
上面的代码定义了三个Node对象,并通过一些基本的逻辑关系组合在一起,下面再用一张图来绘制他们在内存中的关系。
从上图可以看出,起初创建三个Node对象的时候,都是从main开始,各自的指向各自的对象,但是我们额外又增加了一些操作,当代码执行到n1.next = n2,n1指向n2,n2=null,相当于把n2的指向为空,当程序执行到n3 = n2;的时候,相当于n3的引用指向n2的内存。
package com.sunyard.test;
public class StatusTranfer {
public static void test(){
String a = new String("今天天气真好"); //
a = new String("今天天气不好"); //
}
public static void main(String[] args) {
test(); //
}
}
当程序执行到1处的时候,创建了一个String对象“今天天气真好”,引用变量a指向它,这个时候“今天天气真好”处于可达状态,执行到2处代码的时候,"今天天气真好"就处于可恢复状态了,而"今天天气不好"处于可达状态,引用变量a指向它。到这里我们可以得出一个结论,判断一个对象是否可回收的标准就在于该对象是否被引用,因此引用也是JVM进行内存管理的一个重要概念。为了更好地管理对象的引用,从JDK1.2开始,Java在java.lang.ref包下提供了3个类:SoftReference、PhantomReference、WeakReference。它们分别代表了系统对对象的3种引用方式:软引用、虚引用和弱引用。归纳起来,Java语言对对象的引用有如下4种:强引用,软引用,虚引用和弱引用。
强引用:
这是Java程序中最常见的一个方式,程序创建一个对象,并把这个对象赋给一个引用变量,这个引用变量就是强引用。Java程序可通过强引用来访问实际的对象,前面介绍的所有的引用变量都是强引用的方式。当一个对象被一个或一个以上的强引用变量所引用时,它处于可达状态,它不可能被系统垃圾回收机制回收。由于JVM肯定不会回收强引用所引用的Java对象,因此强引用是造成Java内存泄漏的主要原因之一。
软引用:
软引用需要通过SoftReference类来实现,当一个对象只具有软引用时,它有可能被垃圾回收机制回收。对于只有软引用的对象而言,当系统内存空间足够时,它不会被系统回收,程序也可使用该对象;当系统内存空间不足时,系统将会回收它。
使用场景:
当程序需要大量创建某个类的新对象。
例如,需要访问1000个Person对象,可以有两种方式:
1.依次创建1000个Person对象,但只有一个Person引用指向最后一个Person对象;
2.定义一个长度为1000的Person数组,每个数组元素引用一个Person对象。
我们在这里只说明第二种情况,这种情况每个Person对象没有耦合性相对第一种情况来说,但是弱点也很大。如果系统堆内存空间紧张,而1000个Person对象都被强引用引用着,垃圾回收机制也不可能回收它们的堆内存空间,系统性能将变得非常差,甚至因为内存不足导致程序终止。
例如:下面程序创建了一个SoftReference数组,通过SoftReference数组来保存100个Person对象,当系统内存充足时,SoftReference引用和强引用并没有太大的区别。
代码如下:
package com.sunyard.test;
import java.lang.ref.SoftReference;
public class ReferenceTest {
@SuppressWarnings("unchecked")
public static void main(String[] args) {
SoftReference<Person>[] people = new SoftReference[100];
for(int i = 0;i < people.length;i++){
people[i] = new SoftReference<Person>(new Person("名字" + i, (i + 1) * 4 % 100));
}
System.out.println(people[2].get());
System.out.println(people[4].get());
//通知系统进行垃圾回收
System.gc();
System.runFinalization();
//垃圾回收机制运行之后,SoftReference数组里的元素保持不变
System.out.println(people[2].get());
System.out.println(people[4].get());
}
}
class Person {
String name;
int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
}
控制台输出:
Person [name=名字2, age=12]
Person [name=名字4, age=20]
Person [name=名字2, age=12]
Person [name=名字4, age=20]
这个是内存足够的情况,如果我们把堆内存强制命令修改为只有两M,并且循环次数改为10000,其实会看到输出很多null,这个时候软引用的作用就体现出来了,如果这个时候用强引用来创建对象,会抛出虚拟机内存溢出的异常。
弱引用:
弱引用与软英文有点相似,区别在于弱引用所引用对象的生存期更短。弱引用通过WeakReference类实现,弱引用和软引用很像,但弱引用的引用级别更低。对于只有弱引用的对象而言,当系统垃圾回收机制运行时,不管系统内存是否足够,总会回收该对象所占用的内存。当然,并不是说当一个对象只有弱引用时,它就会立即被回收--正如那些失去引用的对象一样,必须等到系统垃圾回收机制运行时候才会被回收。
下面的代码显示了弱引用对象也会被系统垃圾回收的过程。
package com.sunyard.test;
import java.lang.ref.WeakReference;
public class WeakReferenceTest {
public static void main(String[] args) {
//创建一个字符串对象
String str = new String("今天天气真好");
//创建一个弱引用,让此弱引用引用到"今天天气真好"字符串
WeakReference<String> wr = new WeakReference<String>(str); // 1
//切断str引用和"今天天气真好"字符串之间的引用
str = null; // 2
//取出弱引用所引用的对象
System.out.println(wr.get()); // 3
//强制垃圾回收
System.gc();
System.runFinalization();
//再次取出弱引用所引用的对象
System.out.println(wr.get()); //
}
}
这段代码的运行中对象的引用变化如下图所示:

JAVA引用的种类的更多相关文章
- Java引用总结--StrongReference、SoftReference、WeakReference、PhantomReference
Java引用总结--StrongReference.SoftReference.WeakReference.PhantomReference 1 Java引用介绍 Java从1.2版本开始引入了4种引 ...
- Java锁的种类
转载自:---->http://ifeve.com/java_lock_see/ Java锁的种类以及辨析锁作为并发共享数据,保证一致性的工具,在JAVA平台有多种实现(如 synchroniz ...
- 4种Java引用浅解
近期研究Java Cache实现,发现使用到了软引用(SoftReference),不太理解,查阅了JDK文档.代码以及几篇文章.做个小结,如有错误,欢迎指正. 之所以想学习一下Java的几种引用类型 ...
- Java引用类型变量
Java引用类型变量 1.编译时类型:由声明该变量时使用的类型决定 2.执行时类型:由实际赋给该变量的对象决定 类型不一致的假设编译时类型和执行,可能会出现多态性 版权声明:本文博主原创文章.博 ...
- Java引用变量的类型
Java引用变量的类型 1.编译时类型:由声明该变量时使用的类型决定 2.运行时类型:由实际赋给该变量的对象决定 如果编译时类型和运行时类型不一致,就可能出现多态性
- Java引用详解-StrongReference SoftReference WeakReference PhantomReference
1 Java引用介绍 Java从1.2版本开始引入了4种引用,这4种引用的级别由高到低依次为: 强引用 > 软引用 > 弱引用 > 虚引用 ⑴强引用(StrongR ...
- java锁的种类以及辨析(转载)
java锁的种类以及辨析(一):自旋锁 锁作为并发共享数据,保证一致性的工具,在JAVA平台有多种实现(如 synchronized 和 ReentrantLock等等 ) .这些已经写好提供的锁为我 ...
- 浅谈Java引用和Threadlocal的那些事
这篇文章主要介绍了Java引用和Threadlocal的那些事,小编觉得挺不错的,现在分享给大家,也给大家做个参考.一起跟随小编过来看看吧 1 背景 某一天在某一个群里面的某个群友突然提出了一个问 ...
- java引用知识
最近从新拜读<深入理解Java虚拟机:JVM高级特性与最佳实践>这本书,看到有关引用的相关知识,以前没有好的习惯,这次看完在博客上记录下 引用:如果reference类型中的数据存储的数值 ...
随机推荐
- Netty 内存回收之 noCleaner 策略
前言 对于堆外内存,使用 System.gc() 是不靠谱的,依赖老年代 FGC 也是不靠谱的,而且大部分调优指南都设置了 -DisableExplicitGC 禁用 System.gc().所以主动 ...
- 深入出不来nodejs源码-内置模块引入再探
我发现每次细看源码都能发现我之前写的一些东西是错误的,去改掉吧,又很不协调,不改吧,看着又脑阔疼…… 所以,这一节再探,是对之前一些说法的纠正,另外再缝缝补补一些新的内容. 错误在哪呢?在之前的初探中 ...
- SQL Server T—SQL 学生选课数据库SQL语句考试题(45道题)
题目 设有一数据库,包括四个表:学生表(Student).课程表(Course).成绩表(Score)以及教师信息表(Teacher).四个表的结构分别如表1-1的表(一)~表(四)所示,数据如表1 ...
- 彻底理解voliate
1.voliate简介 在上一篇文章中我们深入理解了java关键字synchronized,我们知道在java中还有一大神器就是关键volatile,可以说是和synchronized各领风骚,其中奥 ...
- Java static和final
java提高篇(七)-----关键字static static 在Java中并不存在全局变量的概念,但是我们可以通过static来实现一个“伪全局”的概念,在Java中static表示“全局”或者“静 ...
- java POI excel 导出复合样式(一个单元格两个字体)
前言:java poi 导出 excel 时,需要设置一个单元格有多个字体样式,有点类似于富文本. 想要达到的效果(一个单元格里): 我使用的 poi 版本是 <dependency> & ...
- java.lang.NoSuchMethodError: No static method getFont(Landroid/content/Context;ILandroid/util/TypedValue;ILandroid/widget/TextView;)
global.gradle版本配置文件 原配置 compile_sdk_version = 26 build_tools_version = '26.0.2' target_sdk_version = ...
- JavaScript--表单处理(27)
一 表单介绍 // 在HTML中,表单是由<form>元素来表示的,而在JavaScript中,表单对应的则是HTMLFormElement类型; // HTMLFormElement继承 ...
- linux上,mysql使用聚合函数group by 时报错:SELECT list is not in GROUP BY clause and contains nonaggre的问题
之前在windows上测试是可以正常使用的,但是上传到Linux上后,就报错: Expression # of SELECT list is not in GROUP BY clause and co ...
- 关于初步搭建完成SSH环境之后,JUnit test 测试成功,页面测试时:@Resource 注入的dao为null
这个问题研究了一天,还是因为配置的时候没有认真,一不小心,酿成了大错.当发现的时候感觉好尴尬啊::>_<:: CostAction: package com.tenni.action; i ...