JVM学习(4)非堆的配置参数

2018-07-14  本文已影响0人  陈阳001

除了堆内存以外,虚拟机还有一些内存用于方法区,线程栈和直接内存的使用。它们与堆内存是独立的。虽然和堆内存相比,这些内存空间和应用程序可能关系不是那么密切,但从系统层面来看,有效,合理地配置这些内存参数,对系统性能和稳定性有着重要的作用。

一.元数据区配置(jdk7 的方法区)

用于存放类的元数据,Metaspace使用的是本地内存,而不是堆内存。

二.永久区(方法区)与metadata区的对比:

PermGen
方法区:
1.又叫静态区,跟堆一样,被所有的线程共享。方法区包含所有的class和static变量。
2.方法区中包含的都是在整个程序中永远唯一的元素,如class,static变量。
JAVA 8之前中被称作“Permanent Generation”的特殊区域。这是以前存放例如class的metadata。而,Permgen还存储String之类的额外数据。实际上为JAVA开发者添加了许多麻烦,因为很难预测到底需要多少的空间。这些错误的预测结果表现形式为java.lang.OutOfMemoryError: Permgen space。除非是类似OutOfMemoryError的原因是真的是因为内存泄漏,解决这种问题的简单方法是增加permgen尺寸。下图中设置permgen尺寸的最大值为256M:

java -XX:MaxPermSize=256m

Metaspace
JDK8之前,由永久代实现,主要存放类的信息、常量池、方法数据、方法代码等;JDK8之后,取消了永久代,提出了元空间,并且常量池、静态成员变量等迁移到了堆中;元空间不在虚拟机内存中,而是放在本地内存中。
类的静态变量和Interned Strings 都被转移到了java堆区,
只有class元数据才在元空间。

正如预测metadata是一件纷繁复杂的事情那样,JAVA 8移除了Permanent区,换作Metaspace。从那时起,绝大多数复杂的事情都被移到Java heap区。

类定义文件,现在都存入叫做“Metaspace”的区域中。他相当于本地内存的一块区域。理论上,Metaspace尺寸仅仅受限于JAVA进程可获得本地内存大小。将JAVA开发人员从仅仅在应用多增加一个类就造成java.lang.OutOfMemoryError: Permgen space的困境中解脱出来。需要注意的是这个看起来不受限制没有损失的空间-让Metaspace无限制的增长你会引起内存重交换或者/和本地内存分配失败。

某些场合你希望保护自己,你可以如下图所示限制Metaspace增长,Metaspace尺寸限制在265M:

java -XX:MaxMetaspaceSize=256m

三. 栈配置:

栈是每个线程私有的内存空间。在java虚拟机中可以使用-Xss指定每个线程的栈大小。

四.直接内存配置:

NIO在被广泛使用后,直接内存的使用变得非常普遍。直接内存跳过了java堆,使java程序可以直接访问原生堆空间,因此,从一定程度上加快了内存空间的访问速度。
最大可以直接内存可以使用参数-XX:MaxDirectMemorySize设置,如果不设置,默认是最大堆空间,即-Xmx。当直接内存使用量达到-XX:MaxDirectMemorySize
时,就会触发垃圾回收,如果垃圾回收不能有效释放足够空间,直接内存依然会引起系统的OOM。
一般来说,直接内存的访问速度(读或者写)会快于堆内存。

/**
 *
 * 直接内存配置:
 * NIO在被广泛使用后,直接内存的使用变得非常普遍。直接内存跳过了java堆,使java程序可以直接访问原生堆空间,因此,从一定程度上加快了内存空间的访问速度。
 * 一般来说,直接内存的访问速度(读或者写)会快于堆内存。下面的代码统计了对直接内存和堆内存的读写速度。
 * @author chenyang30
 * @date 2018/7/14
 */
public class AccessDirectBuffer {
    public void directAccess(){
        long starttime=System.currentTimeMillis();
        ByteBuffer b=ByteBuffer.allocateDirect(500);
        for(int i=0;i<100000;i++){
            for(int j=0;j<99;j++){
                b.putInt(j);
            }
            b.flip();
            for(int j=0;j<99;j++){
                b.getInt();
            }
            b.clear();
        }
        long endtime=System.currentTimeMillis();
        System.out.println("testDirectWrite:"+(endtime-starttime));
    }

    public void bufferAccess(){
        long starttime=System.currentTimeMillis();
        ByteBuffer b=ByteBuffer.allocate(500);
        for(int i=0;i<100000;i++){
            for(int j=0;j<99;j++){
                b.putInt(j);
            }
            b.flip();
            for(int j=0;j<99;j++){
                b.getInt();
            }
            b.clear();
        }
        long endtime=System.currentTimeMillis();
        System.out.println("testBufferWrite:"+(endtime-starttime));
    }


    public void directAllocate(){
        long starttime=System.currentTimeMillis();
        for(int i=0;i<200000;i++){
            ByteBuffer b=ByteBuffer.allocateDirect(1000);
        }

        long endtime=System.currentTimeMillis();
        System.out.println("directAllocation:"+(endtime-starttime));
    }


    public void bufferAllocate(){
        long starttime=System.currentTimeMillis();
        for(int i=0;i<200000;i++){
            ByteBuffer b=ByteBuffer.allocateDirect(1000);
        }

        long endtime=System.currentTimeMillis();
        System.out.println("bufferAllocation:"+(endtime-starttime));
    }
    public static void main(String[] args) {
        AccessDirectBuffer alloc=new AccessDirectBuffer();
        alloc.bufferAccess();
        alloc.directAccess();

        alloc.bufferAccess();
        alloc.directAccess();

        alloc.bufferAllocate();
        alloc.directAllocate();

        alloc.bufferAllocate();
        alloc.directAllocate();
    }
}

结果:
testBufferWrite:40
testDirectWrite:26
testBufferWrite:16
testDirectWrite:11
bufferAllocation:156
directAllocation:149
bufferAllocation:359
directAllocation:197

根据耗时可以看出直接内存的访问和空间申请比堆内存快40%左右。

上一篇下一篇

猜你喜欢

热点阅读