通常Java的缓存管理会由垃圾回收器(Java Garbage Collection)定时处理,无须程序员操心。但Java Garbage Collection仅有权回收那些非“强引用”(Strong Reference)类型的类。也就是说,如果程序生成大量的强引用对象,JVM存放对象实例的“堆”(heap)容量不够时(默认64M,可设置),就会抛出java.lang.OutOfMemoryError:
Java heap space的错误,看下面的代码:

编译器报错:Exception in thread "main" java.lang.OutOfMemoryError: Java heap space

JDK用自带的类java.lang.ref.SoftReference提供解决办法,而不是像C++那样自己动手写缓存管理队列。SoftReference将我们新实例化的类转变成“软引用”(Soft Reference)型,以便Garbage Collection能够在JVM的heap不足时,回收那些相对较旧的“软引用”实例,腾出heap空间存放新的对象实例。需要注意的是,尽管“软引用”实例被回收,但它们的Reference已经在JVM中完成登记,一旦它们被再次引用,JVM能够复原它们(仍旧是“软引用”型)。下面的代码显示如何使用SoftReference类:

上述代码可以用:BookShelf shelf = (BookShelf)shelves[i].get();得到BookShelf的实例。这时所有实例都会被创建:

JavaDoc1.6中写道,一个实例的引用类型,可用它在引用链中的可达性(reachable)表示,JVM共有5种可达性:强可到达(strongly reachable);软可到达(softly reachable);弱可到达(weakly reachable);虚可到达(phantomly reachable);不可到达(unreachable). 除了Strong Reference,其他四种对象都可以被回收。

可到达性

从最强到最弱,不同的可到达性级别反映了对象的生命周期。在操作上,可将它们定义如下:

  • 如果某一线程可以不必遍历所有引用对象而直接到达一个对象,则该对象是强可到达 对象。新创建的对象对于创建它的线程而言是强可到达对象(注:这就是第一段代码中BookShelf不能被gc回收的原因:BookShelf对Liberary来说是强可到达对象)。
  • 如果一个对象不是强可到达对象,但通过遍历某一软引用可以到达它,则该对象是软可到达 对象。
  • 如果一个对象既不是强可到达对象,也不是软可到达对象,但通过遍历弱引用可以到达它,则该对象是弱可到达 对象。当清除对某一弱可到达对象的弱引用时,便可以终止此对象了。
  • 如果一个对象既不是强可到达对象,也不是软可到达对象或弱可到达对象,它已经终止,并且某个虚引用在引用它,则该对象是虚可到达 对象。
  • 最后,当不能以上述任何方法到达某一对象时,该对象是不可到达 对象,因此可以回收此对象。

java.lang.ref包中对应还有WeakReference、PhantomReference两个类,有机会再实践下。

import java.lang.ref.SoftReference;

class Book
{
char[] symbols = new char[300000];
} class BookShelf
{
Book[] books = new Book[100]; public BookShelf()
{
for (int i = 0; i < books.length; i++)
{
books[i] = new Book();
}
// TODO Auto-generated constructor stub
}
} public class Library
{
public static void main(String[] args)
{
// BookShelf[] shelves = new BookShelf[50];
SoftReference[] shelves = new SoftReference[50];
for (int i = 0; i < shelves.length; i++)
{
// shelves[i] = new BookShelf();
shelves[i] = new SoftReference(new BookShelf());
System.out.println("Creating bookshelf: " + (i + 1));
}
}
}

《Java的内存管理1》提醒我们:如果需要在同一对象中不停地new一些占用大量内存的实例,务必使用java.lang.ref.SoftReference类对实例进行“包装”,防止JVM因Java Garbage Collection不能回收新建实例,而造成的内存溢出错误。

我想到可以用finalize()方法,观察实例在什么时候会被GC回收(任何继承Object的类都可Override),finalize()会在一个实例即将被GC回收的时候被调用一次,因此可用它记录一个实例的“临终遗言”,我对代码做了如下修改:(顺道实现了java1.5的泛型)

运行结果却出乎意料:

Creating bookshelf: 1

Creating bookshelf: 2

Creating bookshelf: 3

Creating bookshelf: 4

Creating bookshelf: 5

Creating bookshelf: 6

Creating bookshelf: 7

Creating bookshelf: 8

Creating bookshelf: 9

Creating bookshelf: 10

Creating bookshelf: 11

BookShelf: 10 is dying!

BookShelf: 9 is dying!

BookShelf: 8 is dying!

BookShelf: 7 is dying!

BookShelf: 6 is dying!

BookShelf: 5 is dying!

BookShelf: 4 is dying!

BookShelf: 3 is dying!

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space

at edu.thinkingjava.chap4.Book.<init>(Library.java:5)

at edu.thinkingjava.chap4.BookShelf.<init>(Library.java:15)

at edu.thinkingjava.chap4.Library.main(Library.java:28)

BookShelf: 2 is dying!

BookShelf: 1 is dying!

BookShelf: 11 is dying!

即便使用了SoftReference做缓存保护,控制台再次抛出OutOfMemoryError的异常。输出的BookShelf销毁顺序,每次都有不同,颇有多线程的味道。由此看出GC的回收机制充满不确定性。JavaDoc1.6解释如下:

在启用某个对象的 finalize 方法后,将不会执行进一步操作,直到 Java 虚拟机再次确定尚未终止的任何线程无法再通过任何方法访问此对象,其中包括由准备终止的其他对象或类执行的可能操作,在执行该操作时,对象可能被丢弃。 对于任何给定对象,Java 虚拟机最多只调用一次 finalize 方法。

JavaDoc实际上在说:finalize()方法阻挡了GC对BookShelf实例的首次回收,JVM转而去调用finalize()方法,所幸Java 虚拟机最多只调用一次 finalize 方法,当下一次JVM发现内存吃紧,BookShelf实例才能最终被回收。

由此例推断:JVM释放内存的进度被finalize方法耽误了,因此出现内存溢出异常(大家可以试着把代码第19行注释掉,内存又恢复正常了)。

import java.lang.ref.SoftReference;

class Book
{
char[] symbols = new char[300000];
} class BookShelf
{
private int ID; Book[] books = new Book[100]; public BookShelf()
{
for (int i = 0; i < books.length; i++)
{
books[i] = new Book();
}
// TODO Auto-generated constructor stub
} public BookShelf(int ID)
{
this.ID = ID;
for (int i = 0; i < books.length; i++)
{
books[i] = new Book();
}
// TODO Auto-generated constructor stub
} @Override
protected void finalize() throws Throwable
{
// TODO Auto-generated method stub
super.finalize();
System.out.println("BookShelf: " + ID + " is dying!");
}
} public class Library
{
public static void main(String[] args)
{
// BookShelf[] shelves = new BookShelf[50];
SoftReference[] shelves = new SoftReference[50];
for (int i = 0; i < shelves.length; i++)
{
// shelves[i] = new BookShelf();
shelves[i] = new SoftReference(new BookShelf(i + 1));
System.out.println("Creating bookshelf: " + (i + 1));
}
}
}

Java的内存管理1:“并不只有C++程序员关心内存回收”——Java的内存管理2:"不中用的finalize( )方法"的更多相关文章

  1. Java匹马行天下之C国程序员的秃头原因

    Java帝国的崛起 前言: 分享技术之前先请允许我分享一下黄永玉老先生说过的话:“明确的爱,直接的厌恶,真诚的喜欢.站在太阳下的坦荡,大声无愧地称赞自己.” <编程常识知多少> <走 ...

  2. 程序员必须掌握的Java 框架,小白学会之后15k不是问题

    Spring 的核心特性是什么?Spring 优点? Spring 的核心是控制反转(IoC)和面向切面(AOP) Spring 优点: 程序员必须掌握的Java 框架,学会之后50k不是问题 (1) ...

  3. 从程序员到CTO的Java技术路线图 作者:zz563143188

    在技术方面无论我们怎么学习,总感觉需要提升自已不知道自己处于什么水平了.但如果有清晰的指示图供参考还是非常不错的,这样我们清楚的知道我们大概处于那个阶段和水平. Java程序员 高级特性 反射.泛型. ...

  4. 年度Java技术盘点,懂这些技术的程序员2019发展大好

    与一年前一样,Java仍然是最流行的编程语言.据TIOBE的数据显示,几十年来,Java比其他语言更常名列榜首,Java因为它拥有可移植性.可扩展性和庞大的用户社区,所以许多知名互联网公司使用Java ...

  5. MySQL内存表(MEMORY)说明 | 一个PHP程序员的备忘录

    MySQL内存表(MEMORY)说明 | 一个PHP程序员的备忘录 MySQL内存表(MEMORY)说明

  6. 做什么职业,也别做程序员,尤其是Java程序员

    千万别做程序员,尤其别做Java这种门槛低,入门快的程序员(别跟我说Java搞精通了也很牛之类的,原因不解释,做5年以上就知道了),程序员本来就是我见过最坑爹的职业了...Java程序员更是,现在满地 ...

  7. 寻找下一个结点 牛客网 程序员面试金典 C++ java Python

    寻找下一个结点 牛客网 程序员面试金典 C++ java Python 题目描述 请设计一个算法,寻找二叉树中指定结点的下一个结点(即中序遍历的后继). 给定树的根结点指针TreeNode* root ...

  8. 碰撞的蚂蚁 牛客网 程序员面试金典 C++ Java Python

    碰撞的蚂蚁 牛客网 程序员面试金典 C++ Java Python 题目描述 在n个顶点的多边形上有n只蚂蚁,这些蚂蚁同时开始沿着多边形的边爬行,请求出这些蚂蚁相撞的概率.(这里的相撞是指存在任意两只 ...

  9. 检查是否是BST 牛客网 程序员面试金典 C++ java Python

    检查是否是BST 牛客网 程序员面试金典  C++ java Python 题目描述 请实现一个函数,检查一棵二叉树是否为二叉查找树. 给定树的根结点指针TreeNode* root,请返回一个boo ...

  10. java程序员--小心你代码中的内存泄漏

    当你从c&c++转到一门具有垃圾回收功能的语言时,程序员的工作就会变得更加容易,因为你用完对象,他们会被自动回收,但是,java程序员真的不需要考虑内存泄露吗? 其实不然 1.举个例子-看你能 ...

随机推荐

  1. Word在不同电脑排版异常

    Word在不同电脑排版异常 问题描述 今天又有同学向我抱怨用 word 写的论文明明在自己的电脑格式调的好好的,怎么在导师那格式又乱了,害的挨批. 笔者也遇到过该问题,正好趁这次机会简单整理一下. 注 ...

  2. ComfyUI 基础教程(三) —— 应用 Controlnet 精准控制图像生成

    一.前言 你是否有见过下面类似这样的图片: 看起来平平无奇,当你站远点看,或者把眼睛眯成一条缝了看,你会发现,这个图中藏有一些特别的元素.这就是利用了 Ai 绘画中的 ControlNet,实现对图片 ...

  3. 补: Rest 风格请求处理的的内容补充(1)

    补: Rest 风格请求处理的的内容补充(1) Rest风格请求:注意事项和细节 客户端是PostMan 可以直接发送Put,delete等方式请求,可不设置Filter 如果哟啊SpringBoot ...

  4. webpack笔记-webpack之模块module、路径解析、resolve 配置(三)

    module webpack 的初衷是让 js 支持模块化管理,并且将前端中的各种资源都纳入到对应的模块管理中来,所以在 webpack 的设计中,最重要的部分就是管理模块和模块之间的关系. 在 we ...

  5. 【合合TextIn】智能文档处理系列—电子文档解析技术全格式解析

    一.引言 在当今的数字化时代,电子文档已成为信息存储和交流的基石.从简单的文本文件到复杂的演示文档,各种格式的电子文档承载着丰富的知识与信息,支撑着教育.科研.商业和日常生活的各个方面.随着信息量的爆 ...

  6. ASP.NET Core – Web API 冷知识

    Under/Over Posting 参考: .NET Core WebApi Action is executed even with missing properties in the reque ...

  7. MyBatis——案例——查询-查询详情

      查询-查询详情 (根据id获取商品全部信息(即商品对象))          1.编写Mapper接口方法:Brand selectById(int id);            2.编写SQL ...

  8. GIS转码的秋招历程与踩坑经历

      本文介绍地理信息科学(GIS)专业的2024届应届生,在研三上学期期间,寻找后端研发.软件开发等IT方向工作的非科班转码秋招情况.   首先,这篇文章一开始写于2023年年底,当时为了参加一个征文 ...

  9. 智慧矿山IT智能运维自动化解决方案

    矿山企业是国民经济中的重要组成部分,其资源开发和产业链条中涉及的环节与过程非常繁琐和复杂.随着我国矿山企业规模逐年扩大,为了提高其生产效率和降低其生产成本,信息化.数字化建设成为当下矿山企业发展的重要 ...

  10. laravel框架中保留条件搜索

    前段代码 <form action="admin_index" method="get"> <input type="text&qu ...