通常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. mvn install 设定下载的依赖保存的地址

    mvn clean install -P jdk-1.8 -Dmaven.repo.local=/xxxx/repository

  2. Navicat 15 for MySQL 破解教程

    Navicat 15 for MySQL安装包和注册机下载: 安装包:https://kohler.lanzouh.com/irtcd05za1zc 注册机:https://kohler.lanzou ...

  3. 探索 Nuxt Devtools:功能全面指南

    title: 探索 Nuxt Devtools:功能全面指南 date: 2024/9/3 updated: 2024/9/3 author: cmdragon excerpt: 摘要:本文介绍了Nu ...

  4. 神奇的C语言输出12天圣诞节歌词代码

    12天圣诞节程序怎样运行?1988 年,一个令人印象深刻且令人敬畏的 C 代码,代号为 xmas.c,在国际混淆 C 代码竞赛中获胜.该程序甚至比其输出的"压缩"类型还要小,代表了 ...

  5. 【YashanDB知识库】YAS-00103 no free block in dictionary cache

    [问题分类]功能使用 [关键字]YAS-00103,no free block in dictionary cache [问题描述]执行union all 太多子查询导致报错,例子如下: [问题原因分 ...

  6. C++: 如何高效地往unordered_map中插入key-value

    C++: unordered_map 花式插入key-value的5种方式 前言 无意中发现std::unordered_map.std::map等插入key-value对在C++17后竟有了 ins ...

  7. 待补 重要思考:求给无向图定向使得其变为DAG的方案数

    今天比赛考到了,不会,丢了 100 分. rk2,380 -> rk15,280 别问为什么 T4 没过,因为不会 T2. 方法一 \(O(3^n)\) 令 \(f_S\) 为子集 \(S\) ...

  8. JavaScript – Promise

    前言 我学 Promise 的时候, 那时还没有 es6. 曾经还自己实现过. 但时隔多年, 现在 es6 的 promise 已经很完善了. 这篇作为一个简单的复习. (毕竟我已经 1 年多没有写 ...

  9. .NET 7+Angular 4 轻量级新零售进销存系统

    前言 给大家推荐一个专为新零售快消行业打造了一套高效的进销存管理系统. 系统不仅具备强大的库存管理功能,还集成了高性能的轻量级 POS 解决方案,确保页面加载速度极快,提供良好的用户体验. 项目介绍 ...

  10. QT数据可视化框架编程实战之三维曲面图 实时变化的三维曲面图 补天云QT技术培训专家

    QT数据可视化框架编程实战之三维曲面图 实时变化的三维曲面图 补天云QT技术培训专家 简介 本文将介绍QT数据可视化框架编程实战之三维曲面图,本文通过构造一个数据实时变化的三维曲面图的应用实例来展示Q ...