在nio以前,是没有光明正大的做法的,有一个work around的办法是直接访问Unsafe类。如果你使用Eclipse,默认是不允许访问sun.misc下面的类的,你需要稍微修改一下,给Type Access Rules里面添加一条所有类都可以访问的规则:

在使用Unsafe类的时候:

Unsafe f = Unsafe.getUnsafe();

发现还是被拒绝了,抛出异常:

java.lang.SecurityException: Unsafe

正如Unsafe的类注释中写道:

Although the class and all methods are public, use of this class is limited because only trusted code can obtain instances of it.

于是,只能使用反射来做这件事;

        Field f = Unsafe.class.getDeclaredField("theUnsafe");
f.setAccessible(true);
Unsafe us = (Unsafe) f.get(null);
long id = us.allocateMemory(1024 * 1024 * 1024);

其中,allocateMemory返回一个指针,并且其中的数据是未初始化的。如果要释放这部分内存的话,需要调用freeMemory或者reallocateMemory方法。Unsafe对象提供了一系列put/get方法,例如putByte,但是只能一个一个byte地put,我不知道这样会不会影响效率,为什么不提供一个putByteArray的方法呢?

示例:

import sun.misc.Unsafe;

public class ObjectInHeap
{
private long address = 0; private Unsafe unsafe = GetUsafeInstance.getUnsafeInstance(); public ObjectInHeap()
{
address = unsafe.allocateMemory(2 * 1024 * 1024);
} // Exception in thread "main" java.lang.OutOfMemoryError
public static void main(String[] args)
{
while (true)
{
ObjectInHeap heap = new ObjectInHeap();
System.out.println("memory address=" + heap.address);
}
}
}

这段代码会抛出OutOfMemoryError。这是因为ObjectInHeap对象是在堆内存中分配的,当该对象被垃圾回收的时候,并不会释放堆外内存,因为使用Unsafe获取的堆外内存,必须由程序显示的释放,JVM不会帮助我们做这件事情。由此可见,使用Unsafe是有风险的,很容易导致内存泄露。

4、正确释放Unsafe分配的堆外内存

虽然第3种情况的ObjectInHeap存在内存泄露,但是这个类的设计是合理的,它很好的封装了直接内存,这个类的调用者感受不到直接内存的存在。那怎么解决ObjectInHeap中的内存泄露问题呢?可以覆写Object.finalize(),当堆中的对象即将被垃圾回收器释放的时候,会调用该对象的finalize。由于JVM只会帮助我们管理内存资源,不会帮助我们管理数据库连接,文件句柄等资源,所以我们需要在finalize自己释放资源。

import sun.misc.Unsafe;

public class RevisedObjectInHeap
{
private long address = 0; private Unsafe unsafe = GetUsafeInstance.getUnsafeInstance(); // 让对象占用堆内存,触发[Full GC
private byte[] bytes = null; public RevisedObjectInHeap()
{
address = unsafe.allocateMemory(2 * 1024 * 1024);
bytes = new byte[1024 * 1024];
} @Override
protected void finalize() throws Throwable
{
super.finalize();
System.out.println("finalize." + bytes.length);
unsafe.freeMemory(address);
} public static void main(String[] args)
{
while (true)
{
RevisedObjectInHeap heap = new RevisedObjectInHeap();
System.out.println("memory address=" + heap.address);
}
} }

我们覆盖了finalize方法,手动释放分配的堆外内存。如果堆中的对象被回收,那么相应的也会释放占用的堆外内存。这里有一点需要注意下

// 让对象占用堆内存,触发[Full GC
private byte[] bytes = null;

这行代码主要目的是为了触发堆内存的垃圾回收行为,顺带执行对象的finalize释放堆外内存。如果没有这行代码或者是分配的字节数组比较小,程序运行一段时间后还是会报OutOfMemoryError。这是因为每当创建1个RevisedObjectInHeap对象的时候,占用的堆内存很小(就几十个字节左右),但是却需要占用2M的堆外内存。这样堆内存还很充足(这种情况下不会执行堆内存的垃圾回收),但是堆外内存已经不足,所以就不会报OutOfMemoryError。

Java堆外内存之四:直接使用Unsafe类操作堆外内存的更多相关文章

  1. java 非阻塞算法实现基础:unsafe类介绍

    一.为什么要有Unsfae.我们为什么要了解这个类 1. java通常的代码无法直接使用操作底层的硬件,为了使java具备该能力,增加了Unsafe类 2.java的并发包中底层大量的使用这个类的功能 ...

  2. Java中的Unsafe类111

    1.Unsafe类介绍 Unsafe类是在sun.misc包下,不属于Java标准.但是很多Java的基础类库,包括一些被广泛使用的高性能开发库都是基于Unsafe类开发的,比如Netty.Hadoo ...

  3. Conccrent中 Unsafe类原理 以及 原子类AutomicXX的原理以及对Unsafe类的使用

    Unsafe类的介绍 Java中基于操作系统级别的原子操作类sun.misc.Unsafe,它是Java中对大多数锁机制实现的最基础类.请注意,JDK 1.8和之前JDK版本的中sun.misc.Un ...

  4. C Primer Plus之存储类、链接和内存管理

    存储时期即生存周期——变量在内存中保留的时间 变量的作用域和链接一起表明程序的哪些部分可以通过变量名来使用该变量. 注意:生存期和作用域是两个不同的概念. 作用域    作用域描述了程序中可以访问一个 ...

  5. Java堆外内存之一:堆外内存场景介绍(对象池VS堆外内存)

    最近经常有人问我在Java中使用堆外(off heap)内存的好处与用途何在.我想其他面临几样选择的人应该也会对这个答案感兴趣吧. 堆外内存其实并无特别之处.线程栈,应用程序代码,NIO缓存用的都是堆 ...

  6. Java之JVM调优案例分析与实战(3) - 堆外内存导致的溢出错误

    环境:基于B\S的点子考试系统,为了发现客户端能实时地从服务端接收考试数据,系统使用了逆向AJAX技术(也称Comet或Server Side Push),选用CometD1.1.1作为服务端推送框架 ...

  7. Java为什么会引入及如何使用Unsafe

    综述 sun.misc.Unsafe至少从2004年Java1.4开始就存在于Java中了.在Java9中,为了提高JVM的可维护性,Unsafe和许多其他的东西一起都被作为内部使用类隐藏起来了.但是 ...

  8. Java双刃剑之Unsafe类详解

    前一段时间在研究juc源码的时候,发现在很多工具类中都调用了一个Unsafe类中的方法,出于好奇就想要研究一下这个类到底有什么作用,于是先查阅了一些资料,一查不要紧,很多资料中对Unsafe的态度都是 ...

  9. java编程之:Unsafe类

    Unsafe类在jdk 源码的多个类中用到,这个类的提供了一些绕开JVM的更底层功能,基于它的实现可以提高效率.但是,它是一把双刃剑:正如它的名字所预示的那样,它是 Unsafe的,它所分配的内存需要 ...

随机推荐

  1. DevExpress v17.2新版亮点——VCL篇(二)

    用户界面套包DevExpress v17.2日前终于正式发布,本站将以连载的形式为大家介绍各版本新增内容.本文将介绍了DevExpress VCL v17.2 的新功能,快来下载试用新版本! DPI ...

  2. 使用MyEclipse将HTML5移动项目迁移到PhoneGap(三)

    MyEclipse开年钜惠 在线购买低至75折!立即开抢>> [MyEclipse最新版下载] 三.将图像从HTML5项目复制到PhoneGap项目 1. 在PhoneGap项目的www ...

  3. “开始菜单”按钮今年8月将重回Windows 8

    本月早些时候微软明确表示,“开始菜单”将重新回归Windows 8操作系统.尽管微软当时并没有公布具体的时间表,但据熟悉微软内部运作的消息灵通人士透露称,“开始菜单”极有可能将出现在预计于今年8月发布 ...

  4. android源码追踪学习 RecipientsEditor

    RecipientsEditor 新建短信时输入收接者的editor, public class RecipientsEditor extends MultiAutoCompleteTextView  ...

  5. PAT 列车厢调度   (25分)(栈和容器的简单应用)

    1 ====== <--移动方向 / 3 ===== \ 2 ====== -->移动方向 大家或许在某些数据结构教材上见到过“列车厢调度问题”(当然没见过也不要紧).今天,我们就来实际操 ...

  6. postman-进阶使用

    管理请求 保存请求-添加“打开百度首页请求” 设置请求方式为Get,地址为www.baidu.com.点击右侧保存按钮 在弹出的保存请求中设置保存的内容.Collections:百度,Request ...

  7. MyEclipse10 添加反编译JadClipse插件

    工具/原料   MyEclipse10.0.7+net.sf.jadclipse_3.3.0.jar+jad.exe net.sf.jadclipse_3.3.0.jar+jad.exe下载地址:ht ...

  8. Linux运维学习笔记-TCP三次握手和四次挥手

    TCP三次握手:     TCP四次挥手:

  9. 批量分割视频opencv

    前言 视频处理过程中,会用到对等长的视频进行处理,此时要对大视频进行分割. 实现步骤 1.批量读取视频集: 2.视频分割: 测试代码 1.批量读取视频集: /********************* ...

  10. [LeetCode&Python] Problem 496. Next Greater Element I

    You are given two arrays (without duplicates) nums1 and nums2 where nums1’s elements are subset of n ...