Java堆外内存之突破JVM枷锁

对于有Java开发经验的朋友都知道,Java中不需要手动的申请和释放内存,JVM会自动进行垃圾回收;而使用的内存是由JVM控制的。

那么,什么时机会进行垃圾回收,如何避免过度频繁的垃圾回收?如果JVM给的内存不够用,怎么办?

此时,堆外内存登场!利用堆外内存,不仅可以随意操控内存,还能提高网络交互的速度。

背景1:JVM内存的分配

  对于JVM的内存规则,应该是老生常谈的东西了,这里我就简单的说下:

  新生代:一般来说新创建的对象都分配在这里。

  年老代:经过几次垃圾回收,新生代的对象就会放在年老代里面。年老代中的对象保存的时间更久。

  永久代:这里面存放的是class相关的信息,一般是不会进行垃圾回收的。

背景2:JVM垃圾回收

  由于JVM会替我们执行垃圾回收,因此开发者根本不需要关心对象的释放。但是如果不了解其中的原委,很容易内存泄漏,只能两眼望天了!

  垃圾回收,大致可以分为下面几种:

  Minor GC:当新创建对象,内存空间不够的时候,就会执行这个垃圾回收。由于执行最频繁,因此一般采用复制回收机制。

  Major GC:清理年老代的内存,这里一般采用的是标记清除+标记整理机制。

  Full GC:有的说与Major GC差不多,有的说相当于执行minor+major回收,那么我们暂且可以认为Full GC就是全面的垃圾回收吧。

堆外内存?

  堆外内存,其实就是不受JVM控制的内存。相比于堆内内存有几个优势:

  1 减少了垃圾回收的工作,因为垃圾回收会暂停其他的工作(可能使用多线程或者时间片的方式,根本感觉不到)

  2 加快了复制的速度。因为堆内在flush到远程时,会先复制到直接内存(非堆内存),然后在发送;而堆外内存相当于省略掉了这个工作。

  而福之祸所依,自然也有不好的一面:

  1 堆外内存难以控制,如果内存泄漏,那么很难排查

  2 堆外内存相对来说,不适合存储很复杂的对象。一般简单的对象或者扁平化的比较适合。

  

  堆外内存可以通过java.nio的ByteBuffer来创建,调用allocateDirect方法申请即可。参考API地址

 

  至于怎么用,读读API文档就知道啦~  

 

  另外,默认的情况下堆外内存是有一定的限制的,好像是64M吧....

  可以通过设置-XX:MaxDirectMemorySize=10M控制堆外内存的大小:

堆外内存的垃圾回收

  堆外内存,既然可以无限使用,那么会不会用爆内存呢?这个是很有可能的...所以堆外内存的垃圾回收也很重要。

  由于堆外内存并不直接控制于JVM,因此只能等到full GC的时候才能垃圾回收!

  Full GC,一般发生在年老代垃圾回收以及调用System.gc的时候,这样肯定不能满足我们的需求!于是度娘帮助解决了这个问题,网上有朋友十分聪明的利用内部实现接口,反向获取到了一个clear方法!

package xing.test;

import java.nio.ByteBuffer;
import sun.nio.ch.DirectBuffer;

public class NonHeapTest {
    public static void clean(final ByteBuffer byteBuffer) {
        if (byteBuffer.isDirect()) {
           ((DirectBuffer)byteBuffer).cleaner().clean();
        }
  }  

    public static void sleep(long i) {
        try {
              Thread.sleep(i);
         }catch(Exception e) {
              /*skip*/
         }
    }
    public static void main(String []args) throws Exception {
           ByteBuffer buffer = ByteBuffer.allocateDirect(1024 * 1024 * 200);
           System.out.println("start");
           sleep(5000);
           clean(buffer);//执行垃圾回收
//         System.gc();//执行Full gc进行垃圾回收
           System.out.println("end");
           sleep(5000);
    }
}

  这样就能手动的控制回收堆外内存了!其中sun.nio其实是java.nio的内部实现。所以你可能不能通过eclipse的自动排错找到这个包,直接复制

import sun.nio.ch.DirectBuffer;

  就行。

  由于本文整理与网络各种资料,有些不对的地方还请指正,共同探讨!

参考资料

堆外内存的回收

ByteBuffer官方API

本文转自博客园xingoo的博客,原文链接:Java堆外内存之突破JVM枷锁,如需转载请自行联系原博主。

时间: 2024-09-28 15:50:47

Java堆外内存之突破JVM枷锁的相关文章

JVM源码分析之不可控的堆外内存

概述 之前写过篇文章,关于堆外内存的,JVM源码分析之堆外内存完全解读,里面重点讲了DirectByteBuffer的原理,但是今天碰到一个比较奇怪的问题,在设置了-XX:MaxDirectMemorySize=1G的前提下,然后统计所有DirectByteBuffer对象后面占用的内存达到了7G,远远超出阈值,这个问题很诡异,于是好好查了下原因,虽然最终发现是我们统计的问题,但是期间发现的其他一些问题还是值得分享一下的. 不得不提的DirectByteBuffer构造函数 打开DirectBy

JVM源码分析之堆外内存完全解读

概述 广义的堆外内存 说到堆外内存,那大家肯定想到堆内内存,这也是我们大家接触最多的,我们在jvm参数里通常设置-Xmx来指定我们的堆的最大值,不过这还不是我们理解的Java堆,-Xmx的值是新生代和老生代的和的最大值,我们在jvm参数里通常还会加一个参数-XX:MaxPermSize来指定持久代的最大值,那么我们认识的Java堆的最大值其实是-Xmx和-XX:MaxPermSize的总和,在分代算法下,新生代,老生代和持久代是连续的虚拟地址,因为它们是一起分配的,那么剩下的都可以认为是堆外内存

从0到1起步-跟我进入堆外内存的奇妙世界

一.什么是堆外内存 1.堆内内存(on-heap memory) 回顾堆外内存和堆内内存是相对的二个概念,其中堆内内存是我们平常工作中接触比较多的,我们在jvm参数中只要使用-Xms,-Xmx等参数就可以设置堆的大小和最大值,理解jvm的堆还需要知道下面这个公式: 堆内内存 = 新生代+老年代+持久代 如下面的图所示: 在使用堆内内存(on-heap memory)的时候,完全遵守JVM虚拟机的内存管理机制,采用垃圾回收器(GC)统一进行内存管理,GC会在某些特定的时间点进行一次彻底回收,也就是

Java直接(堆外)内存使用详解

本篇主要讲解如何使用直接内存(堆外内存),并按照下面的步骤进行说明: 相关背景-->读写操作-->关键属性-->读写实践-->扩展-->参考说明 希望对想使用直接内存的朋友,提供点快捷的参考. 数据类型 下面这些,都是在使用DirectBuffer中必备的一些常识,暂作了解吧!如果想要深入理解,可以看看下面参考的那些博客. 基本类型长度 在Java中有很多的基本类型,比如: byte,一个字节是8位bit,也就是1B short,16位bit,也就是2B int,32位bit

JVM内存管理:JAVA语言的内存管理概述

引言 内存管理一直是JAVA语言自豪与骄傲的资本,它让JAVA程序员基本上可以彻底忽略与内存管理相关的细节,只专注于业务逻辑.不过世界上不存在十全十美的好事,在带来了便利的同时,也因此引入了很多令人抓狂的内存溢出和泄露的问题. 可怕的事情还不只如此,有些使用其它语言开发的程序员,给JAVA程序员扣上了一个"不懂内存"的帽子,这着实有点让人难以接受.毕竟JAVA当中没有malloc和delete.没有析构函数.没有指针,刚开始接触JAVA的程序员们又怎么可能接触内存这一部分呢,更何况有不

《JVM故障诊断指南》之2 —— 调整合适的Java堆大小的技巧

原文链接 原文作者:Byron Kiourtzoglou 翻译:梅小西(904516706) 在生产系统上决定合适的Java堆大小不是一个容易的操作.许多性能问题的发生都是由于不恰当的Java堆容量的错误调整.这部分将从介绍一些技巧作为开头,它能帮助你在当前的或者新的生产系统上决定最佳的Java堆大小.其中一些技巧对预防OutOfMemoryError问题和内存泄露方面也同样有用. 请注意这些技巧是倾向于"帮助你"决定合适的Java堆大小.因为每一个IT环境都不相同,实际上你是处于最好

java虚拟机-java 垃圾回收 Mark-and-Compact 算法 去碎片如何操作堆中内存的

问题描述 java 垃圾回收 Mark-and-Compact 算法 去碎片如何操作堆中内存的 在压缩堆内存阶段,遍历堆中所有对象并将存活对象重新放入连续的内存地址的 过程中,如果某个存活对象即将放入的地址中存有另一个还没有被移动的存活的对象, java jvm如何进行操作呢? 我看的算法中没有给此方面的信息.它只说存活对象放入 连续的堆中! 大神!!!!求助!!!!!!!!!

Java堆内存的10个要点

我刚开始学习Java编程时,可不知道什么是堆内存或堆空间(heap space),甚至根本不管对象创建时都放在哪里去了.正式了写一些程序后,经常会遇到java.lang.outOfMemoryError等错误,我才开始关注堆内存.对大多数程序员都经历过这样的过程,因为学习一种语言是非常容易来的,但是学习基础是非常难的,因为没有什么特定的流程让你学习编程的每个基础,使你发觉编程的秘诀. 对于程序员来说,知道堆空间,设置堆空间,处理堆空间的outOfMemoryError错误,分析heap dump

java 堆内存 与栈内存

Java把内存分成两种,一种叫做栈内存,一种叫做堆内存 在函数中定义的一些基本类型的变量和对象的引用变量都是在函数的栈内存中分配.当在一段代码块中定义一个变量时,java就在栈中为这个变量分配内存空间,当超过变量的作用域后,java会自动释放掉为该变量分配的内存空间,该内存空间可以立刻被另作他用. 堆内存用于存放由new创建的对象和数组.在堆中分配的内存,由java虚拟机自动垃圾回收器来管理.在堆中产生了一个数组或者对象后,还可以在栈中定义一个特殊的变量,这个变量的取值等于数组或者对象在堆内存中