小程序 转义_为内存密集型应用程序转义JVM堆

小程序 转义

如果您曾经分配过大型Java堆,您就会知道在某个时候(通常从大约4 GiB开始),您将开始遇到垃圾回收暂停的问题。

我不会详细说明为什么在JVM中会出现暂停,但是总之,当JVM进行完整的收集并且您有很大的堆时,就会发生暂停。 随着堆的增加,这些集合可能会变长。

解决此问题的最简单方法是调整JVM垃圾回收参数,以匹配特定应用程序的内存分配和释放行为。 这有点晦涩难懂,需要仔细测量,但是可能有很大的堆,同时又避免了大多数旧式垃圾回收。 如果您想了解有关垃圾收集调优的更多信息,请查阅JVM GC调优指南 。 如果您总体上对GC真的很感兴趣,那么这是一本很棒的书: The Garbage Collection Handbook 。

有一些JVM实现可以保证比Sun VM(例如Zing JVM)少得多的暂停时间,但通常会以其他系统成本为代价,例如增加内存使用量和单线程性能。 易于配置和低gc保证仍然非常吸引人。 出于本文的目的,我将使用Java中的内存中缓存或存储的示例,主要是因为我在过去使用这些技术中的一些时已经构建了一对。

我们假设我们有一个基本的缓存接口定义,如下所示:

import java.io.Externalizable;public interface Cache<K extends Externalizable, V extends Externalizable> {public void put(K key, V value);public V get(K key);
}

对于这个简单的示例,我们要求键和值是可外部化的,而不是像IRL这样的。

我们将展示如何使用此缓存的不同实现,以不同的方式将数据存储在内存中。 实现此缓存的最简单方法是使用Java集合:

import java.io.Externalizable;
import java.util.HashMap;
import java.util.Map;public class CollectionCache<K extends Externalizable, V extends Externalizable> implements Cache<K, V> {private final Map<K, V> backingMap = new HashMap<K, V>();public void put(K key, V value) {backingMap.put(key, value);}public V get(K key) {return backingMap.get(key);}
}

该实现是直接的。 但是,随着地图大小的增加,我们将分配大量对象(并取消分配),我们使用的是盒装原语,它占用了更多的内存空间,因此原语和地图需要不时调整大小。 当然,我们可以简单地通过使用基于基元的映射来改进此实现。 它会使用较少的内存和对象,但仍会占用堆中的空间并可能对堆进行分区,如果由于其他原因执行完整的GC,则会导致更长的暂停时间。

让我们看看不使用堆来存储相似数据的其他方法:

  • 使用单独的过程来存储数据 。 可能是通过套接字或Unix套接字连接的Redis或Memcached实例。 实施起来相当简单。
  • 使用内存映射文件将数据卸载到磁盘 。 操作系统是您的朋友,它将做很多繁重的工作来预测接下来从文件中读取的内容以及与文件的接口,就像一大堆数据一样。
  • 使用本机代码并通过JNI或JNA访问它 。 通过JNI,您将获得更好的性能,并通过JNA易于使用。 需要您编写本机代码。
  • 使用 NIO包中直接分配的缓冲区
  • 使用特定于Sun的Unsafe类直接从Java代码访问内存。

我将重点介绍本文仅使用Java的解决方案,直接分配的缓冲区和Unsafe类。

直接分配的缓冲区

在Java NIO中开发高性能网络应用程序时,直接分配缓冲区非常有用,并且广泛使用。 通过直接在堆外部分配数据,在许多情况下,您可以编写软件,使这些数据实际上永远不会碰到堆。

创建一个新的直接分配的缓冲区非常简单:

int numBytes = 1000;
ByteBuffer buffer = ByteBuffer.allocateDirect(numBytes);

创建新缓冲区后,可以用几种不同的方式来操作缓冲区。 如果您从未使用过Java NIO缓冲区,那么绝对值得一看,因为它们确实很棒。

除了填充,清空和标记缓冲区中不同点的方法外,您还可以选择在缓冲区上使用不同的视图而不是ByteBuffer –例如, buffer.asLongBuffer()为您提供了在ByteBuffer上的视图,您可以在该视图上buffer.asLongBuffer()操作元素。

那么如何在我们的Cache示例中使用它们呢? 有很多种方法,最直接的方法是将值记录的序列化/外部化形式存储在一个大数组中,以及指向该数组中记录的偏移量和大小的键映射。

它可能看起来像这样(非常宽松的方法,缺少实现并假设记录大小固定):

import java.io.Externalizable;
import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.Map;public class DirectAllocatedCache<K extends Externalizable, V extends Externalizable> implements Cache<K,V> {private final ByteBuffer backingMap;private final Map<K, Integer> keyToOffset;private final int recordSize;public DirectAllocatedCache(int recordSize, int maxRecords) {this.recordSize = recordSize;this.backingMap = ByteBuffer.allocateDirect(recordSize * maxRecords);this.keyToOffset = new HashMap<K, Integer>();}public void put(K key, V value) {if(backingMap.position() + recordSize < backingMap.capacity()) {keyToOffset.put(key, backingMap.position());store(value);}   }public V get(K key) {int offset = keyToOffset.get(key);if(offset >= 0)return retrieve(offset);throw new KeyNotFoundException();}public V retrieve(int offset) {byte[] record = new byte[recordSize];int oldPosition = backingMap.position();backingMap.position(offset);backingMap.get(record);backingMap.position(oldPosition);//implementation left as an exercisereturn internalize(record);}public void store(V value) {byte[] record = externalize(value);backingMap.put(record);}
}

如您所见,此代码有许多限制:固定的记录大小,固定的支持映射大小,完成外部化的方式有限,难以删除和重用空间等。尽管其中某些方式可以通过巧妙的方法来克服以字节数组表示记录(也可以在直接分配的缓冲区中表示keyToOffset映射)或处理删除操作(我们可以实现自己的SLAB分配器),其他诸如调整支持映射大小的操作很难克服。 一个有趣的改进是将记录实现为记录和字段的偏移量,从而减少了我们仅按需复制和复制的数据量。

请注意,JVM对直接分配的缓冲区使用的内存量施加了限制。 您可以使用-XX:MaxDirectMemorySize选项进行调整。 查看ByteBuffer javadocs

不安全

直接从Java管理内存的另一种方法是使用隐藏的Unsafe类。 从技术上讲,我们不应该使用它,它是特定于实现的,因为它位于sun软件包中,但是提供的可能性是无限的。
Unsafe给我们带来的是直接从Java代码分配,取消分配和管理内存的能力。 我们还可以获取实际的指针,并将它们在本机代码和Java代码之间互换传递。

为了获得一个不安全的实例,我们需要走一些弯路:

private Unsafe getUnsafeBackingMap() {try {Field f = Unsafe.class.getDeclaredField('theUnsafe');f.setAccessible(true);return (Unsafe) f.get(null);} catch (Exception e) { }return null;
}

一旦有了不安全因素,我们可以将其应用于我们先前的Cache示例:

import java.io.Externalizable;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;import sun.misc.Unsafe;public class UnsafeCache<K extends Externalizable, V extends Externalizable> implements Cache<K, V> {private final int recordSize;private final Unsafe backingMap;private final Map<K, Integer> keyToOffset;private long address;private int capacity;private int currentOffset;public UnsafeCache(int recordSize, int maxRecords) {this.recordSize = recordSize;this.backingMap = getUnsafeBackingMap();this.capacity = recordSize * maxRecords;this.address = backingMap.allocateMemory(capacity);this.keyToOffset = new HashMap<K, Integer>();}public void put(K key, V value) {if(currentOffset + recordSize < capacity) {store(currentOffset, value);keyToOffset.put(key, currentOffset);currentOffset += recordSize;}}public V get(K key) {int offset = keyToOffset.get(key);if(offset >= 0)return retrieve(offset);throw new KeyNotFoundException();}public V retrieve(int offset) {byte[] record = new byte[recordSize];//Inefficientfor(int i=0; i<record.length; i++) {record[i] = backingMap.getByte(address + offset + i);}//implementation left as an exercisereturn internalize(record);}public void store(int offset, V value) {byte[] record = externalize(value);//Inefficientfor(int i=0; i<record.length; i++) {backingMap.putByte(address + offset + i, record[i]);}}private Unsafe getUnsafeBackingMap() {try {Field f = Unsafe.class.getDeclaredField('theUnsafe');f.setAccessible(true);return (Unsafe) f.get(null);} catch (Exception e) { }return null;}
}

有很多改进的空间,您需要手动执行许多操作,但是功能非常强大。 您还可以显式释放和重新分配以这种方式分配的内存,这使您可以以与C相同的方式编写一些代码。

查看javadocs中的Unsafe

结论

有很多方法可以避免在Java中使用堆,并以此方式使用更多的内存。 您无需执行此操作,而且我个人看到运行20GiB-30GiB且已进行适当调整的JVM,并且没有长时间的垃圾回收暂停,但这非常有趣。

如果您想了解一些项目如何将其用于我在此处编写的基本(并且未经测试,几乎写在餐巾纸上)缓存代码,请查看EHCache的BigMemory或Apache Cassandra,它们也将Unsafe用于此类方法。

参考:在Java Advent Calendar博客上,我们的JCG合作伙伴 Ruben Badaro 从JVM堆转出了内存密集型应用程序 。

翻译自: https://www.javacodegeeks.com/2012/12/escaping-the-jvm-heap-for-memory-intensive-applications.html

小程序 转义

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/351090.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Ubuntu 进入单用户模式—修改启动项利器

偶尔会遇到Ubuntu无法正常启动的情况&#xff0c;这时候需修改某些文件让系统正常启动&#xff0c;如果直接进入 recovery 模式&#xff0c;默认是文件权限只读&#xff0c;无法修改文件。这是我们需要进入recovery 的单用户模式&#xff0c;获得修改文件的权限。 1、重启ubunt…

博客教程中百度网盘地址

博客中百度网盘地址&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1ncGK5fXMBmmkuZGbBSohBw 提取码&#xff1a;v67x 复制这段内容后打开百度网盘手机App&#xff0c;操作更方便哦 转载于:https://www.cnblogs.com/PIESat/p/10593654.html

只有经验丰富的开发人员才能教您有关Java的5件事

深入研究Java之前需要了解的所有内容的概述 有许多工具&#xff0c;方法&#xff0c;环境和功能会改变您处理代码的方式&#xff0c;而这些通常是在学年期间不会遇到的。 尽管它在Java开发世界中迈出了第一步&#xff0c;但大多数实际学习都是在工作中进行的。 在接下来的文章…

conda命令

创建环境 conda create --name snowflake biopython 这条命令将会给biopython包创建一个新的环境&#xff0c;位置在/envs/snowflakes 很多跟在--后边常用的命令选项&#xff0c;可以被略写为一个短线加命令首字母。所以--name选项和-n的作用是一样的。通过conda -h或conda –-…

bzoj4443:[Scoi2015]小凸玩矩阵

传送门 二分答案是显然的啊&#xff0c;然后对于比二分出的值大的直接跑最大匹配&#xff0c;然后判定就好了 代码&#xff1a; #include<cstdio> #include<iostream> #include<algorithm> #include<vector> #include<cstring> using namespace …

GDB调试及其调试脚本的使用

一、GDB调试 1.1. GDB 概述 GDB是GNU开源组织发布的一个强大的UNIX下的程序调试工具。或许&#xff0c;各位比较喜欢那种图形界面方式的&#xff0c;像VC、BCB等IDE的调试&#xff0c;但如果你是在UNIX/Linux平台下做软件&#xff0c;你会发现GDB这个调试工具有比VC、BCB的图形…

Python中xlrd模块解析

xlrd 导入模块 import xlrd 2、打开指定的excel文件&#xff0c;返回一个data对象 data xlrd.open_workbook(file) #打开excel表&#xff0c;返回data对象 3、通过data对象可以得到各个sheet对象(一个excel文件可以有多个sheet&#xff0c;每个sheet就是一…

数学中常见的距离

1. 欧氏距离 最常见的两点之间或多点之间的距离表示法&#xff0c;又称之为欧几里得度量&#xff0c;它定义于欧几里得空间中&#xff0c;如点 x (x1,...,xn) 和 y (y1,...,yn) 之间的距离为&#xff1a; (1) 二维平面上两点a(x1,y1)与b(x2,y2)间的欧氏距离&#xff1a; (…

使用Spring Boot和Heroku在20分钟内完成Java的单点登录

建筑物身份管理&#xff0c;包括身份验证和授权&#xff1f; 尝试Stormpath&#xff01; 我们的REST API和强大的Java SDK支持可以消除您的安全风险&#xff0c;并且可以在几分钟内实现。 注册 &#xff0c;再也不会建立auth了&#xff01; 大规模更新 我最初为本文撰写的代码…

OO第一次单元总结

第一次总结性博客 16071070 陈泽寅 2019.3.23 一、第一单元所学总结 首先先来总结一下第一单元我所学到的知识以及所感所悟。第一个单元&#xff0c;是我第一次接触JAVA语言&#xff0c;并且再使用了几次之后&#xff0c;就被这门语言的独有的魅力以及简便的用法所深深吸引。下…

汇编语言学习笔记(十二)-浮点指令

浮点数如何存储浮点寄存器浮点数指令浮点计算例子浮点高级运算CMOV移动指令 浮点数如何存储 浮点数的运算完全不同于整数&#xff0c;从寄存器到指令&#xff0c;都有一套独特的处理流程&#xff0c;浮点单元也称作x87 FPU。 现在看浮点数的表示方式&#xff0c;我们所知道的&a…

人工智能简述

人工智能研究的方向之一&#xff0c;是以所谓 “专家系统” 为代表的&#xff0c;用大量 “如果-就” &#xff08;If - Then&#xff09; 规则定义的&#xff0c;自上而下的思路。   人工神经网络 &#xff08; Artifical Neural Network&#xff09;&#xff0c;标志着另外…

Mockito 的使用

转自&#xff1a;Mockito 中文文档 ( 2.0.26 beta ) 转自&#xff1a;手把手教你 Mockito 的使用 参数匹配器 Argument Matcher(参数匹配器) Mockito通过equals()方法&#xff0c;来对方法参数进行验证。但是有时候我们需要更加灵活的参数需求&#xff0c;比如&#xff0c;匹配…

以SYSTEM用户运行CMD

在SCCM 经常会以NT AUTHOR\SYSTEM帐户操作。 安以下步骤可以以SYSTEM帐户打开一个CMD窗口。 1. 从微软网站下载PSTool。 2. 以管理员运行CMD&#xff0c;进入到解压的PSTool目录。 3. 运行psexec -i -s cmd.exe 4. 在新打开的CMD中运行whoami。 注&#xff1a;这个指令可以让你…

matlab cell

如果p为一个数&#xff0c;那么h(1)p,是没有问题的。 如果p为一个向量&#xff0c;那么h(1,:)p是没有问题的。 如果p是一个矩阵的话&#xff0c;上面的两种赋值方法都是会有错误的。 那么要如何处理呢&#xff1f; 这时就用到了cell数据类型了。cell的每个单元都可以存储任何数…

jboss 不适用内置日志_适用于孩子,父母和祖父母的JBoss HornetQ –第1章

jboss 不适用内置日志现在与HornetQ合作已经快4年了&#xff0c;我认为现在该分享我到目前为止所学的部分知识了。 这篇文章的主要目的不是重写官方文档 &#xff0c;而是以简单的方式阐明我们在PaddyPower中最常用的概念。 什么是HornetQ HornetQ是JMS实现。 JMS是一种面向…

Spring Cloud微服务笔记(四)客户端负载均衡:Spring Cloud Ribbon

客户端负载均衡&#xff1a;Spring Cloud Ribbon 一、负载均衡概念 负载均衡在系统架构中是一个非常重要&#xff0c;并且是不得不去实施的内容。因为负载均衡对系统的高可用性、 网络压力的缓解和处理能力的扩容的重要手段之一。通常所说的负载均衡指的是服务端负载均衡&#…

matlab cell,fix,floor,round取整的几种方式

ceil函数的作用是朝正无穷方向取整&#xff0c;即将m/n的结果向正无穷方向取整&#xff0c;如m/n3.12&#xff0c;则ceil(m/n)的结果为4。 类似的函数有如下几个&#xff1a; fix&#xff1a;朝零方向取整&#xff0c;如fix(-1.3)-1;fix(1.3)1; floor&#xff1a;朝负无穷方…