如何在Java中分配超过-Xmx限制的内存

本文主要介绍Java中几种分配内存的方法。我们会看到如何使用sun.misc.Unsafe来统一操作任意类型的内存。以前用C语言开发的同学通常都希望能在Java中通过较底层的接口来操作内存,他们一定会对本文中要讲的内容感兴趣。

如果你对Java内存优化比较感兴趣,可以看下[url=http://java-performance.info/memory-allocation-in-java/]这篇文章[/url],以及它的姊妹篇:[url=http://it.deepinmind.com]一[/url], [url=http://it.deepinmind.com]二[/url]。http://java-performance.info/memory-consumption-of-java-data-types-2/

[b]数组分配的上限[/b]

Java里数组的大小是受限制的,因为它使用的是int类型作为数组下标。这意味着你无法申请超过Integer.MAX_VALUE(2^31-1)大小的数组。这并不是说你申请内存的上限就是2G。你可以申请一个大一点的类型的数组。比如:


final long[] ar = new long[ Integer.MAX_VALUE ];


这个会分配16G -8字节,如果你设置的-Xmx参数足够大的话(通常你的堆至少得保留50%以上的空间,也就是说分配16G的内存,你得设置成-Xmx24G。这只是一般的规则,具体分配多大要看实际情况)。

不幸的是,在Java里,由于数组元素的类型的限制,你操作起内存来会比较麻烦。在操作数组方面,ByteBuffer应该是最有用的一个类了,它提供了读写不同的Java类型的方法。它的缺点是,目标数组类型必须是byte[],也就是说你分配的内存缓存最大只能是2G。

[b]把所有数组都当作byte数组来进行操作[/b]

假设现在2G内存对我们来说远远不够,如果是16G的话还算可以。我们已经分配了一个long[],不过我们希望把它当作byte数组来进行操作。在Java里我们得求助下C程序员的好帮手了——sun.misc.Unsafe。这个类有两组方法:getN(object, offset),这个方法是要从object偏移量为offset的位置获取一个指定类型的值并返回它,N在这里就是代表着那个要返回值的类型,而putN(Object,offset,value)方法就是要把一个值写到Object的offset的那个位置。

不幸的是,这些方法只能获取或者设置某个类型的值。如果你从数组里拷贝数据,你还需要unsafe的另一个方法,copyMemory(srcObject, srcOffset, destObject,destOffet,count)。这和System.arraycopy的工作方式类似,不过它拷贝的是字节而不是数组元素。

想通过sun.misc.Unsafe来访问数组的数据,你需要两个东西:

[list]
[*]数组对象里数据的偏移量
[*]拷贝的元素在数组数据里的偏移量
[/list]

Arrays和Java别的对象一样,都有一个对象头,它是存储在实际的数据前面的。这个头的长度可以通过unsafe.arrayBaseOffset(T[].class)方法来获取到,这里T是数组元素的类型。数组元素的大小可以通过unsafe.arrayIndexScale(T[].class) 方法获取到。这也就是说要访问类型为T的第N个元素的话,你的偏移量offset应该是arrayOffset+N*arrayScale。

你可以看下[url=http://it.deepinmind.com]这篇文章[/url]里,UnsafeMemory类使用Unsafe访问内存的那个例子。


我们来写个简单的例子吧。我们分配一个long数组,然后更新它里面的几个字节。我们把最后一个元素更新成-1(16进制的话是0xFFFF FFFF FFFF FFFF),然再逐个清除这个元素的所有字节。


final long[] ar = new long[ 1000 ];
final int index = ar.length - 1;
ar[ index ] = -1; //FFFF FFFF FFFF FFFF

System.out.println( "Before change = " + Long.toHexString( ar[ index ] ));

for ( long i = 0; i < 8; ++i )
{
unsafe.putByte( ar, longArrayOffset + 8L * index + i, (byte) 0);
System.out.println( "After change: i = " + i + ", val = " + Long.toHexString( ar[ index ] ));
}


想运行上面 这个例子的话,得在你的测试类里加上下面的静态代码块:


private static final Unsafe unsafe;
static
{
try
{
Field field = Unsafe.class.getDeclaredField("theUnsafe");
field.setAccessible(true);
unsafe = (Unsafe)field.get(null);
}
catch (Exception e)
{
throw new RuntimeException(e);
}
}

private static final long longArrayOffset = unsafe.arrayBaseOffset(long[].class);


输出的结果是:



Before change = ffffffffffffffff
After change: i = 0, val = ffffffffffffff00
After change: i = 1, val = ffffffffffff0000
After change: i = 2, val = ffffffffff000000
After change: i = 3, val = ffffffff00000000
After change: i = 4, val = ffffff0000000000
After change: i = 5, val = ffff000000000000
After change: i = 6, val = ff00000000000000
After change: i = 7, val = 0


[b]sun.misc.Unsafe的内存分配[/b]

上面也说过了,在纯Java里我们的能分配的内存大小是有限的。这个限制在Java的最初版本里就已经定下来了,那个时候人们都不敢相像分配好几个G的内存是什么情况。不过现在已经是大数据的时代了,我们需要更多的内存。在Java里,想获取更多的内存有两个方法:

[list]
[*]分配许多小块的内存,然后逻辑上把它们当作一块连续的大内存来使用。
[*]使用sun.misc.Unsafe.allcateMemory(long)来进行内存分配。
[/list]

第一个方法只是从算法的角度来看比较有意思一点,所以我们还是来看下第二个方法。


sun.misc.Unsafe提供了一组方法来进行内存的分配,重新分配,以及释放。它们和C的malloc/free方法很像:

[list]
[*]long Unsafe.allocateMemory(long size)——分配一块内存空间。这块内存可能会包含垃圾数据(没有自动清零)。如果分配失败的话会抛一个java.lang.OutOfMemoryError的异常。它会返回一个非零的内存地址(看下面的描述)。
[*]Unsafe.reallocateMemory(long address, long size)——重新分配一块内存,把数据从旧的内存缓冲区(address指向的地方)中拷贝到的新分配的内存块中。如果地址等于0,这个方法和allocateMemory的效果是一样的。它返回的是新的内存缓冲区的地址。
[*]Unsafe.freeMemory(long address)——释放一个由前面那两方法生成的内存缓冲区。如果address为0什么也不干 。
[/list]

这些方法分配的内存应该在一个被称为单寄存器地址的模式下使用:Unsafe提供了一组只接受一个地址参数的方法(不像双寄存器模式,它们需要一个Object还有一个偏移量offset)。通过这种方式分配的内存可以比你在-Xmx的Java参数里配置的还要大。

[b]注意:Unsafe分配出来的内存是无法进行垃圾回收的。你得把它当成一种正常的资源,自己去进行管理。[/b]

下面是使用Unsafe.allocateMemory分配内存的一个例子,同时它还检查了整个内存缓冲区是不是可读写的:


final int size = Integer.MAX_VALUE / 2;
final long addr = unsafe.allocateMemory( size );
try
{
System.out.println( "Unsafe address = " + addr );
for ( int i = 0; i < size; ++i )
{
unsafe.putByte( addr + i, (byte) 123);
if ( unsafe.getByte( addr + i ) != 123 )
System.out.println( "Failed at offset = " + i );
}
}
finally
{
unsafe.freeMemory( addr );
}


正如你所看见的,使用sun.misc.Unsafe你可以写出非常通用的内存访问的代码:不管是Java里分配的何种内存,你都可以随意读写任意类型的数据。

原创文章转载请注明出处:[url=http://it.deepinmind.com]http://it.deepinmind.com[/url]

[url=http://java-performance.info/memory-allocation-in-java/]英文原文链接[/url]

想及时了解博客更新,可以关注我的微博[url=http://it.deepinmind.com]Java译站[/url]
简单了解sun.misc.Unsafe 这里简单了解下sun.misc.Unsafe,jdk中的类很多地方都有用到它,它是许多类的实现的关键,知道这个类干嘛的,对一些源码的阅读很有帮助。 sun.misc.Unsafe算是Java留下的后门,能提供相当强大的操作,但是又不提供专业的文档。看名字就知道,sun本身就不推荐使用这个类。一般应用级的代码都不应该使用它,框架级别的,用这个的很多,比如jdk自身,一些框架netty,最常见的框架 阅读详情

相关推荐

Java中的魔法类:sun.misc.Unsafe示例详解

Java是一个安全的开发工具,它阻止开发人员犯很多低级的错误,而大部份的错误都是基于内存管理方面的。如果你想搞破坏,可以使用Unsafe这个类。下面这篇文章主要给大家介绍了关于Java中魔法类:sun.misc.Unsafe的相关资料,需要的朋友可以参考下

Caused by: java.lang.NoSuchMethodException: sun.misc.Unsafe.defineClass(java.lang.String,[B,int,int,...

出现此异常的原因是:java.lang.NoSuchMethodException:sun.misc.Unsafe.defineClass(java.lang.String,[B,int,int,java.lang.ClassLoader,java.security.ProtectionDomain)

weixin_42588555的博客 2466

手把手教你用ZEMAX复现贝塞尔光束:从Thorlabs锥透镜到Edmund透镜的完整光路搭建

本文详细介绍了如何在ZEMAX光学设计软件中复现贝塞尔光束,从Thorlabs锥透镜到Edmund透镜的完整光路搭建过程。通过逐步指导,帮助研究人员和工程师掌握光学设计技巧,实现无衍射特性的贝塞尔光束生成,适用于激光加工和生物成像等领域。

weixin_33737774的博客 181

OAuth 2.0报错解决:NoSuchMethodException: sun.misc.Unsafe.defineClass****java.security.ProtectionDomain

1、先看报错 主要是这句: Caused by: java.lang.NoSuchMethodException: sun.misc.Unsafe.defineClass(java.lang.String,[B,int,int,java.lang.ClassLoader,java.security.ProtectionDomain) 2024-03-04 15:12:23.657 ERROR 33805 --- [ main] c.s.x.b.v2.runtime.reflect.op

换条路吧,编程没意思的~ 2450

AUTOSAR网络管理配置详解:Vector DaVinci工具全面讲解

手把手带你用Vector DaVinci完成AUTOSAR网络管理模块的配置,涵盖NmChannel、NmPdu、重复帧抑制等关键参数设置,深入解析CAN总线上的NM报文调度与唤醒机制,助你扎实掌握AUTOSAR网络管理核心实践。

weixin_42504649的博客 168

基于 STM32 设计的 HC05 蓝牙音箱

本项目介绍如何使用 STM32 微控制器和 HC05 蓝牙模块设计一个简单的蓝牙音箱。该音箱可以接收来自手机或其他蓝牙设备的音频数据,并通过扬声器播放。该项目展示了如何使用 STM32 微控制器和 HC05 蓝牙模块设计一个简单的蓝牙音箱。易于实现功能实用可扩展性强如果您需要开发一个蓝牙音箱,该项目可以作为参考。

走向CTO的路上... 2471

Java中的Unsafe

Java和C++语言的一个重要区别就是Java中我们无法直接操作一块内存区域,不能像C++中那样可以自己申请内存和释放内存。Java中的Unsafe类为我们提供了类似C++手动管理内存的能力。 Unsafe类,全限定名是sun.misc.Unsafe,从名字中我们可以看出来这个类对普通程序员来说是“危险”的,一般应用开发者不会用到这个类。 Unsafe类是"final"的,不允许继承。且构造函数是private的: public final class Unsafe { private .

ywb201314的专栏 396

IEC61850规约客户端软件开发实战(第六章)

IEC 61850标准中的GOOSE(Generic Object Oriented Substation Event,面向通用对象的变电站事件)功能是变电站自动化系统的核心通信机制,主要用于实现智能电子设备(IED)间高可靠性、低延时的实时信号传输。本章简述其核心功能与技术特点。

半青年的博客 1778

关于Docker中设置Java应用的JVM

背景 运行在 Docker 容器中的 Java 应用经常会被操作系统 kill,但 JVM 没有 OOM 日志,下面是一个 Java 应用的容器因为超过了 cgroup 的限制被 kill: # dmesg -T [Sun Mar 22 10:26:23 2020] Memory cgroup out of memory: Kill process 25086 (java) score 1838 or sacrifice child [Sun Mar 22 10:26:23 2020] Killed pro

web13618542420的博客 589

UE5接入在线直播视频源,如hls(m3u8)格式

UE5接入在线直播视频源

专注GIS开发 9499

4 屏 + 8 摄像头一开,谁在悄悄拖慢你的座舱域控?

如果你觉得座舱卡顿:❌ 别第一反应怪 SoC❌ 别盲目堆算力。

weixin_43199439的博客 148

线上的oom,调整虚拟机参数以及堆默认内存分配

线上的oom,调整虚拟机参数

一点光辉的博客 1147

【异常】Coze/扣子 Workflow 调用报 4200 错误深度解析

Coze API 的。

奶爸的编程之路,也就一周冷个三天 301

jvm学习(5)

直接内存溢出 代码: /** * VM Args:-Xmx20M -XX:MaxDirectMemorySize=10M * */ public class DirectMemoryOOM { private static final int _1MB = 1024 * 1024; public static void main(String[] args) thr...

红血哥 266

辐射制冷无规散射结构非球形散射体的结构优化与设计准则

本文针对辐射制冷材料中非球形散射体的光散射特性开展研究,提出了一种基于体积散射截面和不对称因子的综合评价方法。通过建立简化模型分析球体、圆柱体、长方体等六种几何形状在太阳波段的散射性能,发现金字塔形和圆锥形具有更强的后向散射能力。研究创新性地提出"反射因子SR=S(1-g)"这一评价参数,该参数与宏观反射率高度相关,为非球形散射体的优化设计提供了定量依据。仿真结果表明,合理选择散射体形状可显著提升辐射制冷材料的太阳反射率,为实现高效被动制冷提供了新的结构设计思路。

huanghm88的专栏 79

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

java 内存进程 jvm,JVM vs Java进程消耗的本机内存总内存使用量

I have a tiny java console application which I would like to optimize in terms of memory usage. It is being run with Xmx set to only 64MB. The overall memory usage of the process according to differen...

weixin_35702533的博客 218

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

JVM系列解决JVM最大内存设置问题

你知道JVM内存最大能调多大吗,这里和大家分享一下JVM最大内存方面的内容,Java虚拟机具有一个堆,堆是运行时数据区域,所有类实例和数组的内存均从此处分配。堆是在Java虚拟机启动时创建的。 JVM内存最大能调多大 今天分析了当前比较流行的几个不同公司不同版本JVM最大内存,得出来的结果如下: 公司JVM版本最大内存(兆)client最大内存(兆)server SUN1.5.x14921520 SUN1.5.5(Linux)26342660 SUN1.4.215641564 SUN1.4.2(Linux

web18334137065的博客 1461

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

JVM 内存区域内存溢出

文章目录内存溢出以及堆栈溢出总结环境JDK 1.8 Win10x64JAVA堆溢出方法区,运行时常量池 内存溢出以及堆栈溢出 简单认识一下2种内存异常OutOfMemoryError,StackOverflowError 来自JDK Doc的解释: public class OutOfMemoryError extends VirtualMachineError Thrown when the Java Virtual Machine cannot allocate an object because it

Cui's Blog 182

无法创建java虚拟机_java虚拟机创建失败的原因整理

创建java虚拟机失败的解决方法解决问题的步骤:1、从eclipse文件夹中打开eclipse.ini文件2、修改–launcher.XXMaxPermSize属性3、将值改为512m即可配置文件格式:-startupplugins/org.eclipse.equinox.launcher_1.3.0.v20120522-1813.jar--launcher.libraryplugins/org....

weixin_34552525的博客 4865

kettle启动“Error: could not create the Java Virtual Machine”

因为我的操作系统是32bit,而Ketttle的Spoon脚本中,默认是PENTAHO_DI_JAVA_OPTIONS="-Xms1024m" "-Xmx2048m"。   而2048是超过32bit的最大值,将PENTAHO_DI_JAVA_OPTIONS="-Xms512m" "-Xmx1024m"。 还不够则再改小点:  而2048是超过32bit的最大值,将PENTAHO_DI_JAVA_...

weixin_33939843的博客 853

linux 查看java_opts_Linux Tomcat 设置 JAVA_OPTS 异常

Linux:CentOS 6.6内存情况: Tomcat:apache-tomcat-7.0.61.tar.gzbin/catalina.sh 配置declare-xJAVA_OPTS=%JAVA_OPTS%-Xms1260m-Xmx1790m-XX:PermSize=1024m-XX:MaxPermSize=1024m-XX:+UseConcMarkSweepGC-XX:+CMS...

weixin_42220669的博客 368
上一篇: 那些看不见的死锁
下一篇: 如何在Java中分配超过-Xmx限制的内存
spidercoco
博客等级 码龄18年 47粉丝 0原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值