JVM初探使用堆外内存减少FullGCWord文档格式.docx

上传人:b****6 文档编号:21723735 上传时间:2023-01-31 格式:DOCX 页数:18 大小:216.83KB
下载 相关 举报
JVM初探使用堆外内存减少FullGCWord文档格式.docx_第1页
第1页 / 共18页
JVM初探使用堆外内存减少FullGCWord文档格式.docx_第2页
第2页 / 共18页
JVM初探使用堆外内存减少FullGCWord文档格式.docx_第3页
第3页 / 共18页
JVM初探使用堆外内存减少FullGCWord文档格式.docx_第4页
第4页 / 共18页
JVM初探使用堆外内存减少FullGCWord文档格式.docx_第5页
第5页 / 共18页
点击查看更多>>
下载资源
资源描述

JVM初探使用堆外内存减少FullGCWord文档格式.docx

《JVM初探使用堆外内存减少FullGCWord文档格式.docx》由会员分享,可在线阅读,更多相关《JVM初探使用堆外内存减少FullGCWord文档格式.docx(18页珍藏版)》请在冰豆网上搜索。

JVM初探使用堆外内存减少FullGCWord文档格式.docx

05.

*/

publicclassDirectByteBufferAppextendsAbstractAppInvoker{

@Test

@Override

publicvoidinvoke(Object...param){

Map<

String,FeedDO>

map=createInHeapMap(SIZE);

//moveinoff-heap

byte[]bytes=serializer.serialize(map);

ByteBufferbuffer=ByteBuffer.allocateDirect(bytes.length);

buffer.put(bytes);

buffer.flip();

//forgc

map=null;

bytes=null;

System.out.println("

writedown"

);

//moveoutfromoff-heap

byte[]offHeapBytes=newbyte[buffer.limit()];

buffer.get(offHeapBytes);

deserMap=serializer.deserialize(offHeapBytes);

for(inti=0;

i<

SIZE;

++i){

Stringkey="

key-"

+i;

FeedDOfeedDO=deserMap.get(key);

checkValid(feedDO);

if(i%10000==0){

read"

+i);

}

free(buffer);

privateMap<

createInHeapMap(intsize){

longcreateTime=System.currentTimeMillis();

map=newConcurrentHashMap<

>

(size);

size;

FeedDOvalue=createFeed(i,key,createTime);

map.put(key,value);

returnmap;

}

由JDK提供的堆外内存访问API只能申请到一个类似一维数组的ByteBuffer,JDK并未提供基于堆外内存的实用数据结构实现(如堆外的Map、Set),因此想要实现Cache的功能只能在write()时先将数据put()到一个堆内的HashMap,然后再将整个Map序列化后MoveIn到DirectMemory,取缓存则反之.由于需要在堆内申请HashMap,因此可能会导致多次FullGC.这种方式虽然可以使用堆外内存,但性能不高、无法发挥堆外内存的优势.

幸运的是开源界的前辈开发了诸如Ehcache、MapDB、ChronicleMap等一系列优秀的堆外内存框架,使我们可以在使用简洁API访问堆外内存的同时又不损耗额外的性能.

其中又以Ehcache最为强大,其提供了in-heap、off-heap、on-disk、cluster四级缓存,且Ehcache企业级产品(BigMemoryMax/BigMemoryGo)实现的BigMemory也是Java堆外内存领域的先驱.

示例2:

MapDBAPI实现堆外Cache

publicclassMapDBAppextendsAbstractAppInvoker{

privatestaticHTreeMap<

mapDBCache;

static{

mapDBCache=DBMaker.hashMapSegmentedMemoryDirect()

.expireMaxSize(SIZE)

.make();

FeedDOfeed=createFeed(i,key,System.currentTimeMillis());

mapDBCache.put(key,feed);

FeedDOfeedDO=mapDBCache.get(key);

结果&

分析

DirectByteBufferApp

S0S1EOPYGCYGCTFGCFGCTGCT

0.000.005.2278.5759.85192.902137.25110.153

thelastonejstatofMapDBApp

0.000.038.020.3844.461710.23800.0000.238

运行DirectByteBufferApp.invoke()会发现有看到很多FullGC的产生,这是因为HashMap需要一个很大的连续数组,Old区很快就会被占满,因此也就导致频繁FullGC的产生.

而运行MapDBApp.invoke()可以看到有一个DirectMemory持续增长的过程,但FullGC却一次都没有了.

实验:

使用堆外内存减少FullGC

实验环境

java-version

javaversion"

1.7.0_79"

Java(TM)SERuntimeEnvironment(build1.7.0_79-b15)

JavaHotSpot(TM)64-BitServerVM(build24.79-b02,mixedmode)

VMOptions

-Xmx512M

-XX:

MaxDirectMemorySize=512M

+PrintGC

+UseConcMarkSweepGC

+CMSClassUnloadingEnabled

CMSInitiatingOccupancyFraction=80

+UseCMSInitiatingOccupancyOnly

实验数据

170W条动态(FeedDO).

实验代码

第1组:

in-heap、affectbyGC、noserialize

ConcurrentHashMapApp

publicclassConcurrentHashMapAppextendsAbstractAppInvoker{

privatestaticfinalMap<

cache=newConcurrentHashMap<

();

//write

Stringkey=String.format("

key_%s"

i);

FeedDOfeedDO=createFeed(i,key,System.currentTimeMillis());

cache.put(key,feedDO);

//read

FeedDOfeedDO=cache.get(key);

GuavaCacheApp类似,详细代码可参考完整项目.

第2组:

off-heap、notaffectbyGC、needserialize

EhcacheApp

publicclassEhcacheAppextendsAbstractAppInvoker{

privatestaticCache<

cache;

ResourcePoolsresourcePools=ResourcePoolsBuilder.newResourcePoolsBuilder()

.heap(1000,EntryUnit.ENTRIES)

.offheap(480,MemoryUnit.MB)

.build();

CacheConfiguration<

configuration=CacheConfigurationBuilder

.newCacheConfigurationBuilder(String.class,FeedDO.class,resourcePools)

cache=CacheManagerBuilder.newCacheManagerBuilder()

.withCache("

cacher"

configuration)

.build(true)

.getCache("

String.class,FeedDO.class);

Objecto=cache.get(key);

checkValid(o);

MapDBApp与前同.

第3组:

off-process、notaffectbyGC、serialize、affectbyprocesscommunication

LocalRedisApp

publicclassLocalRedisAppextendsAbstractAppInvoker{

privatestaticfinalJediscache=newJedis("

localhost"

6379);

privatestaticfinalIObjectSerializerserializer=newHessian2Serializer();

byte[]value=serializer.serialize(feedDO);

cache.set(key.getBytes(),value);

write"

byte[]value=cache.get(key.getBytes());

FeedDOfeedDO=serializer.deserialize(value);

结果分析

对比前面几组数据,可以有如下总结:

将长生命周期的大对象(如cache)移出heap可大幅度降低FullGC次数与耗时;

使用off-heap存储对象需要付出serialize/deserialize成本;

将cache放入分布式缓存需要付出进程间通信/网络通信的成本(UNIXDomain/TCPIP)

附:

off-heap的Ehcache能够跑出比in-heap的HashMap/Guava更好的成绩确实是我始料未及的O(∩_∩)O~,但确实这些数据和堆内存的搭配导致in-heap的FullGC太多了,当heap堆开大之后就肯定不是这个结果了.因此在使用堆外内存降低FullGC前,可以先考虑是否可以将heap开的更大.

性能测试框架

在main函数启动时,扫描com.vdian.se.apps包下的所有继承了AbstractAppInvoker的类,然后使用Javassist为每个类生成一个代理对象:

当invoke()方法执行时首先检查他是否标注了@Test注解(在此,我们借用junit定义好了的注解),并在执行的前后记录方法执行耗时,并最终对比每个实现类耗时统计.

依赖

<

dependency>

<

groupId>

mons<

/groupId>

artifactId>

commons-proxy<

/artifactId>

version>

${commons.proxy.version}<

/version>

/dependency>

org.javassist<

javassist<

${javassist.version}<

com.caucho<

hessian<

${hessian.version}<

com.google.guava<

guava<

${guava.version}<

junit<

${junit.version}<

启动类:

OffHeapStarter

*@since2017/1/1上午10:

47.

publicclassOffHeapStarter{

String,Long>

STATISTICS_MAP=newHashMap<

publicstaticvoidmain(String[]args)throwsIOException,IllegalAccessException,InstantiationException{

Set<

Class<

?

classes=PackageScanUtil.scanPackage("

com.vdian.se.apps"

for(Class<

clazz:

classes){

AbstractAppInvokerinvoker=createProxyInvoker(clazz.newInstance());

invoker.invoke();

//System.gc();

*********************statistics**********************"

for(Map.Entry<

entry:

STATISTICS_MAP.entrySet()){

method["

+entry.getKey()+"

]totalcost["

+entry.getValue()+"

]ms"

privatestaticAbstractAppInvokercreateProxyInvoker(Objectinvoker){

ProxyFactoryfactory=wJavassistProxyFactory();

Class<

superclass=invoker.getClass().getSuperclass();

Objectproxy=factory

.createInterceptorProxy(invoker,newProfileInterceptor(),newClass[]{superclass});

return(AbstractAppInvoker)proxy;

privatestaticclassProfileInterceptorimplementsInterceptor{

publicObjectintercept(Invocationinvocation)throwsThrowable{

clazz=invocation.getProxy().getClass();

Methodmethod=clazz.getMethod(invocation.getMethod().getName(),Object[].class);

Objectresult=null;

if(method.isAnnotationPresent(Test.class)

&

&

method.getName().equals("

invoke"

))

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 高等教育 > 工学

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1