mmap用的是 jvmjvm 内存 使用吗

Java 不能计算出 JVM 所使用的内存,和空闲内存么? - ITeye问答
Runtime runtime = Runtime.getRuntime();
System.out.println(runtime.totalMemory());
System.out.println(runtime.freeMemory()); // 1
List&Object& list = new ArrayList&Object&();
for(int i = 0;i&10;i++){
list.add(new Object());
Object ob = new Object(); // 2
runtime = Runtime.getRuntime();
System.out.println(runtime.totalMemory());
System.out.println(runtime.freeMemory()); // 3
在 1 和 3 处所获取的值竟然是一样的。很不能理解,而且打开 2 处注释后 freeMemory() 反而曾大了。求教
问题补充:myali88 写道你创建的对象太少,看不出内存变化。而且当你调用runtime.freeMemory()并不能完全说明剩下多少内存,因为GC任何时候都有可能运行。有可能,你创建完10对象后下次到调用runtime.freeMemory()之间,GC已经运行过,回收了内存。
runtime.freeMemory() 返回的是字节。即使只创建一个 Object 也是要消耗内存的。何止我创建了10个。何来“创建的对象太少,看不出内存变化”。我在代码中中用了一个 ArrayList 和 11 个 Object 如果这些对象就能是 GC 运行。那 java 还能用么?所以 “而且当你调用runtime.freeMemory()并不能完全说明剩下多少内存,因为GC任何时候都有可能运行。有可能,你创建完10对象后下次到调用runtime.freeMemory()之间,GC已经运行过,回收了内存。” 没有说服力。
问题补充:myali88 写道并不是你创建的对象引起了GC,GC是JVM自动运行的,你不可能控制。
这么说吧~~~我如果在程序中只创建了一个 Object ,你认为 GC 会工作么?就算它工作了,可此时我创建的这个 Object 在后面如果仍然存在对其的引用。那么该 Object 所占得内存的是不可能不释放的。
例如:
System.gc();
Runtime runtime = Runtime.getRuntime();
System.out.println(runtime.totalMemory());
System.out.println(runtime.freeMemory());
Object object = new Object();
System.out.println(runtime.totalMemory());
System.out.println(runtime.freeMemory());
System.out.println(object.hashCode());
两次所获取的 freeMemory() 仍旧是一样的
[url][/url]
引用
我在代码中中用了一个 ArrayList 和 11 个 Object 如果这些对象就能是 GC 运行。那 java 还能用么?
并不是你创建的对象引起了GC,GC是JVM自动运行的,你不可能控制。
引用
所以 “而且当你调用runtime.freeMemory()并不能完全说明剩下多少内存,因为GC任何时候都有可能运行。有可能,你创建完10对象后下次到调用runtime.freeMemory()之间,GC已经运行过,回收了内存。” 没有说服力。
这样的话,你要好好查查JVM的内存相关知识了。
另外,其实根本不用上面的方法,从JVM对象内存规范,我们就能知道,一个new Object()是占用8 bytes , 一个new ArrayList()占用80 bytes ,所以上面情实际上占用了160 bytes。
建议你看看这篇文章。
楼上正解。
你创建的对象太少,看不出内存变化。而且当你调用runtime.freeMemory()并不能完全说明剩下多少内存,因为GC任何时候都有可能运行。有可能,你创建完10对象后下次到调用runtime.freeMemory()之间,GC已经运行过,回收了内存。
引用
打开 2 处注释后 freeMemory() 反而曾大了
我测试了一下,打开也不会增大。我用JDK 1.6.0_18
已解决问题
未解决问题博客分类:
java处理大文件,一般用BufferedReader,BufferedInputStream这类带缓冲的Io类,不过如果文件超大的话,更快的方式是采用。
是java nio引入的文件内存映射方案,读写性能极高。NIO最主要的就是实现了对异步操作的支持。其中一种通过把一个套接字通道(SocketChannel)注册到一个选择器(Selector)中,不时调用后者的选择(select)方法就能返回满足的选择键(SelectionKey),键中包含了SOCKET事件信息。这就是select模型。
SocketChannel的读写是通过一个类叫ByteBuffer(java.nio.ByteBuffer)来操作的.这个类本身的设计是不错的,比直接操作byte[]方便多了. ByteBuffer有两种模式:直接/间接.间接模式最典型(也只有这么一种)的就是HeapByteBuffer,即操作堆内存 (byte[]).但是内存毕竟有限,如果我要发送一个1G的文件怎么办?不可能真的去分配1G的内存.这时就必须使用"直接"模式,即 MappedByteBuffer,文件映射.
先中断一下,谈谈操作系统的内存管理.一般操作系统的内存分两部分:物理内存;虚拟内存.虚拟内存一般使用的是页面映像文件,即硬盘中的某个(某些)特殊的文件.操作系统负责页面文件内容的读写,这个过程叫"页面中断/切换". MappedByteBuffer也是类似的,你可以把整个文件(不管文件有多大)看成是一个ByteBuffer.MappedByteBuffer 只是一种特殊的 ByteBuffer ,即是ByteBuffer的子类。 MappedByteBuffer 将文件直接映射到内存(这里的内存指的是虚拟内存,并不是物理内存)。通常,可以映射整个文件,如果文件比较大的话可以分段进行映射,只要指定文件的那个部分就可以。三种方式:
FileChannel提供了map方法来把文件影射为内存映像文件: MappedByteBuffer map(int mode,long position,long size); 可以把文件的从position开始的size大小的区域映射为内存映像文件,mode指出了 可访问该内存映像文件的方式:READ_ONLY,READ_WRITE,PRIVATE.
a. READ_ONLY,(只读): 试图修改得到的缓冲区将导致抛出 ReadOnlyBufferException.(MapMode.READ_ONLY) b. READ_WRITE(读/写): 对得到的缓冲区的更改最终将传播到文件;该更改对映射到同一文件的其他程序不一定是可见的。 (MapMode.READ_WRITE)c. PRIVATE(专用): 对得到的缓冲区的更改不会传播到文件,并且该更改对映射到同一文件的其他程序也不是可见的;相反,会创建缓冲区已修改部分的专用副本。 (MapMode.PRIVATE)三个方法:a. fore();缓冲区是READ_WRITE模式下,此方法对缓冲区内容的修改强行写入文件b. load()将缓冲区的内容载入内存,并返回该缓冲区的引用c. isLoaded()如果缓冲区的内容在物理内存中,则返回真,否则返回假三个特性:
调用信道的map()方法后,即可将文件的某一部分或全部映射到内存中,映射内存缓冲区是个直接缓冲区,继承自ByteBuffer,但相对于ByteBuffer,它有更多的优点:a. 读取快b. 写入快c. 随时随地写入下面来看代码:
import java.io.FileInputS
import java.io.FileOutputS
import java.nio.ByteB
import java.nio.MappedByteB
import java.nio.channels.FileC
public class MapMemeryBuffer {
public static void main(String[] args) throws Exception {
ByteBuffer byteBuf = ByteBuffer.allocate(1024 * 14 * 1024);
byte[] bbb = new byte[14 * 1024 * 1024];
FileInputStream fis = new FileInputStream("e://data/other/UltraEdit_17.00.0.1035_SC.exe");
FileOutputStream fos = new FileOutputStream("e://data/other/outFile.txt");
FileChannel fc = fis.getChannel();
long timeStar = System.currentTimeMillis();
fc.read(byteBuf);
System.out.println(fc.size()/1024);
long timeEnd = System.currentTimeMillis();
System.out.println("Read time :" + (timeEnd - timeStar) + "ms");
timeStar = System.currentTimeMillis();
fos.write(bbb);
timeEnd = System.currentTimeMillis();
System.out.println("Write time :" + (timeEnd - timeStar) + "ms");
fos.flush();
fc.close();
fis.close();
运行结果:
Read time :24ms
Write time :21ms
我们把标注1和2语句注释掉,换成它们下面的被注释的那条语句,再来看运行效果。14235
Read time :2ms
Write time :0ms
可以看出速度有了很大的提升。MappedByteBuffer的确快,但也存在一些问题,主要就是内存占用和文件关闭等不确定问题。被MappedByteBuffer打开的文件只有在垃圾收集时才会被关闭,而这个点是不确定的。在javadoc里是这么说的:A mapped byte buffer and the file mapping that it represents remain valid until the buffer itself
is garbage-collected.这里提供一种解决方案:
AccessController.doPrivileged(new PrivilegedAction() {
public Object run() {
Method getCleanerMethod = buffer.getClass().getMethod("cleaner", new Class[0]);
getCleanerMethod.setAccessible(true);
sun.misc.Cleaner cleaner = (sun.misc.Cleaner)
getCleanerMethod.invoke(byteBuffer, new Object[0]);
cleaner.clean();
} catch (Exception e) {
e.printStackTrace();
return null;
关于MappedByteBuffer资源释放问题
JDK1.4中加入了一个新的包:NIO(java.nio.*)。这个库最大的功能(我认为)就是增加了对异步套接字的支持。其实在 其他语言中,包括在最原始的SOCKET实现(BSD SOCKET),这是一个早有的功能:异步回调读/写事件,通过选择器动态选择感兴趣的事件,等等。
先谈谈操作系统的内存管理。一般操作系统的内存分两部分:物理内存;虚拟内存。虚拟内存一般使用的是页面映像文件,即硬盘中的某个(某些)特殊的文件.操作系统负责页面文件内容的读写,这个过程叫"页面中断/切换"。
MappedByteBuffer也是类似的,你可以把整个文件(不管文件有多大)看成是一个ByteBuffer。这是一个很好的设计,除了令人头疼的一点在后面会讲到。
java.nio.MappedByteBuffer
MappedByteBuffer是一个比较方便使用的类。其内容是文件的内存映射区域。映射的字节缓冲区是通过 方法创建的。映射的字节缓冲区和它所表示的文件映射关系在该缓冲区本身成为垃圾回收缓冲区之前一直保持有效。此类用特定于内存映射文件区域的操作扩展
类。 这个类本身的设计是不错的,比直接操作byte[]方便多了。
ByteBuffer有两种模式:直接/间接。间接模式最典型(也只有这么一种)的就是HeapByteBuffer,即操作堆内存(byte [])。但是内存毕竟有限,如果我要发送一个1G的文件怎么办?不可能真的去分配1G的内存.这时就必须使用"直接"模式,即 MappedByteBuffer,文件映射。
在JDK API文档中这样描述的:
全部或部分映射的字节缓冲区可能随时成为不可访问的,例如,如果我们截取映射的文件。试图访问映射的字节缓冲区的不可访问区域将不会更改缓冲区 的内容,并导致在访问时或访问后的某个时刻抛出未指定的异常。因此强烈推荐采取适当的预防措施,以避免此程序或另一个同时运行的程序对映射的文件执行操作 (读写文件内容除外)。
MappedByteBuffer只能通过调用FileChannel的map()取得,再没有其他方式.但是令人奇怪的是,SUN提供了map()却没有提供unmap().这样会导致什么后果呢?
这样,问题就出现了。通过MappedByteBuffer实现文件复制功能非常容易,可以用以下方法来实现。
//文件复制
public void copyFile(String filename,String srcpath,String destpath)throws IOException {
File source = new File(srcpath+"/"+filename);
File dest = new File(destpath+"/"+filename);
FileChannel in = null, out =
in = new FileInputStream(source).getChannel();
out = new FileOutputStream(dest).getChannel();
long size = in.size();
MappedByteBuffer buf = in.map(FileChannel.MapMode.READ_ONLY, 0, size);
out.write(buf);
in.close();
out.close();
source.delete();//文件复制完成后,删除源文件
}catch(Exception e){
e.printStackTrace();
} finally {
in.close();
out.close();
但是如果要实现文件文件复制完成后,删除源文件,以上方法就有问题。因为在source.delete()时,会返回false,删除失败,主 要原因是变量buf仍然有源文件的句柄,文件处于不可删除状态。既然MappedByteBuffer是从FileChannel中map()出来的,为 什么它又不提供unmap()呢?SUN自己也没有讲清楚为什么。O'Reilly的&&Java NIO&&中说是因为"安全"的原因,但是到底unmap()会怎么不安全,作者也没有讲清楚。
在sun网站也有相应的BUG报告:bug id:4724038链接为,但是sun自己不认为是BUG,而只是一个RFE(Request For Enhancement),有待改进。
好在有个叫bellomi的网友提出了一个解决方法,我也测试过,可以实现期望的功能。具体实现代码如下:
public static void clean(final Object buffer) throws Exception {
AccessController.doPrivileged(new PrivilegedAction() {
public Object run() {
Method getCleanerMethod = buffer.getClass().getMethod("cleaner",new Class[0]);
getCleanerMethod.setAccessible(true);
sun.misc.Cleaner cleaner =(sun.misc.Cleaner)getCleanerMethod.invoke(buffer,new Object[0]);
cleaner.clean();
} catch(Exception e) {
e.printStackTrace();
不知道为什么SUN不提供ByteBuffer的派生。毕竟这是一个很实用的类,如果允许派生,那么我就可以操作的就不仅仅限于堆内存和文件了,我可以扩展到任何存储设备。
浏览 40644
浏览: 1360904 次
来自: 北京
我的也是拦截不了service层
讲的不错,很详细,如果quartz定时任务类采用不继承任何类的 ...
你知道eclipse调试怎么可以回调吗?有时候总是调快了,不能 ...
(window.slotbydup=window.slotbydup || []).push({
id: '4773203',
container: s,
size: '200,200',
display: 'inlay-fix'3222人阅读
Java(20)
Java内存区域 JVM运行时数据区
&&&&&& 在前面的一些文章了解到javac编译的大体过程、Class文件结构、以及JVM字节码指令。
&&&&&& 下面我们详细了解Java内存区域:先说明JVM规范定义的JVM运行时分配的数据区有哪些,然后分别介绍它们的特点,并指出给出一些HotSpot虚拟机实现的不同点和调整参数。
1、Java内存区域概述
1-2、C/C++与Java程序开发的内存管理
&&&&&& 在内存管理领域,C/C++程序开发与Java程序开发有着完全不同的理念:
1、C/C++程序开发
&&&&&& 自己管理内存是一项基础的工作;
&&&&&& 自已分配内存,但也得自己来及时回收;
&&&&&& 比较自由,但多了些工作量,且容易出现内存泄露和内存溢出等问题;
2、Java程序开发
&&&&&& JVM管理内存,不需要自己手动分配内存和释放内存;
&&&&&& 不容易出现内存泄露和内存溢出;
&&&&&& 一旦出现问题不容易排查,所以得了解JVM是怎么使用内存;
1-2、Java内存区域与JVM运行时数据区
&&&&&& 如上图, Java虚拟机规范定义了字节码执行期间使用的各种运行时数据区,即JVM在执行Java程序的过程中,会把它管理的内存划分为若干个不同的数据区域,包括:
&&&&&&程序计数器、java虚拟机栈、本地方法栈、java堆、方法区、运行时常量池;
&&&&&& 从线程共享角度来说,可以分为两类:
1、所有线程共享的数据区
&&&&&& 方法区、运行时常量池、java堆;
&&&&&& 这些数据区域是在Java虚拟机启动时创建的,只有当Java虚拟机退出时才会被销毁;
2、线程间隔离的数据区
&&&&&& 程序计数器、java虚拟机栈、本地方法栈、
&&&&&& 这些数据区域是每个线程的&私有&数据区,每个线程都有自己的,不与其他线程共享;
&&&&&& 每个线程的数据区在创建线程时创建,并在线程退出时被销毁;
3、另外,还一种特殊的数据区
&&&&&&直接内存--使用Native函数库直接分配的堆外内存;
&&&&&& 即Java内存区域 = JVM运行时数据区 +直接内存。
2、Java各内存区域说明
&&&&&& 上面图片展示的是JVM规范定义的运行时数据概念模型,实际上JVM的实现可能有所差别,下面在介绍各内存数据区时会给出一些HotSpot虚拟机实现的不同点和调整参数。
2-1、程序计数器
&&&&&&&程序计数器(Program Counter Register),简称PC计数器;
1、生存特点
&&&&&& 每个线程都需要一个独立的PC计数器,生命周期与所属线程相同,各线程的计数器互不影响;
&&&&&&JVM字节码解释器通过改变这个计数器的值来选取线程的下一条执行指令;
3、存储内容
&&&&&& JVM的多线程是通过线程轮流切换并分配处理器执行时间的方式来实现的;
&&&&&& 在任意时刻,一个线程只会执行一个方法的代码(称为该线程的当前方法(Current Method));&&&&&&&&
(A)、如果这个方法是Java方法,那PC计数器就保存JVM正在执行的字节码指令的地址;
(B)、如果该方法是native的,那PC计数器的值是空(undefined);
4、内存分配特点
&&&&&& PC计数器占用较小的内存空间;
&&&&&& 容量至少应当能保存一个returnAddress类型的数据或者一个与平台相关的本地指针的值;
5、异常情况
&&&&&& 唯一一个JVM规范中没有规定会抛出OutOfMemoryError情况的区域;
2-2、Java虚拟机栈
&&&&&& Java虚拟机栈(Java Virtual Machine Stack,JVM Stack),指常说的栈内存(Stack);
&&&&&& 和Java堆指的堆内存(Heap),都是需要重点关注的内存区域;
1、生存特点
&&&&&& 每个线程都有一个私有的,生命周期与所属线程相同;
&&&&&& 描述的是Java方法执行的内存模型,与传统语言中(如C/C++)的栈类似;
&&&&&& 在方法调用和返回中也扮演了很重要的角色;
3、存储内容
&&&&&& 用于保存方法的栈帧(Stack Frame);
&&&&&& 每个方法从调用到执行结束,对应其栈帧在JVM栈上的入栈到出栈的过程;
& &&栈帧:
&&&&&& 每个方法执行时都会创建一个栈帧,随着方法调用而创建(入栈),随着方法结束而销毁(出栈);
&&&&&& 栈帧是方法运行时的基础结构;
&&&&&& 栈帧用于存储局部变量表、操作数栈、动态连接、方法出口等信息;
(A)、局部变量表
&&&&&& 局部变量表(Local Variables Table)是一组变量值存储空间,用于存放方法参数和方法内部定义的局部变量。
&&&&&& 这些都是在编译期可知的数据,所以一个方法调用时,在JVM栈中分配给该方法的局部变量空间是完全确定的,运行中不改变;
&&&&&& 一个方法分配局部变量表的最大容量由Class文件中该方法的Code属性的max_locals数据项确定;
(B)、操作数栈
&&&&&& 操作数栈(Operand Stack)简称操作栈,它是一个后进先出(Last-In-First-Out,LIFO)栈;
&&&&&& 在方法的执行过程中,会有各种字节码指令往操作数栈中写入和提取内容(任意类型的值),也就是入栈/出栈操作;
&&&&&& 在方法调用的时候,操作数栈也用来准备调用方法的参数以及接收方法返回结果;
&&&&&& 一个方法的操作数栈长度由Class文件中该方法的Code属性的max_stacks数据项确定;
(C)、动态链接
&&&&&& 每一个栈帧内部都包含一个指向运行时常量池的引用,来支持当前方法的执行过程中实现动态链接 (Dynamic Linking);
&&&&&& 在 Class 文件里面,描述一个方法调用了其他方法,或者访问其成员变量是通过符号引用(Symbolic Reference)来表示的;
&&&&&&动态链接的作用就是将这些符号引用所表示的方法转换为实际方法的直接引用(除了在类加载阶段解析的一部分符号);
4、内存分配特点
&&&&&& 因为除了栈帧的出栈和入栈之外,JVM栈从来不被直接操作,所以栈帧可以在堆中分配;
&&&&&& JVM栈所使用的内存不需要保证是连续的;
&&&&&& JVM规范允许JVM栈被实现成固定大小的或者是根据计算动态扩展和收缩的:
(A)、固定大小
&&&&&& 如果JVM栈是固定大小的,则当创建新线程的栈时,可以独立地选择每个JVM栈的大小;
(B)、动态扩展或收缩
&&&&&& 在动态扩展或收缩JVM栈的情况下,JVM实现应该提供调节JVM栈最大和最小内存空间的手段;
两种情况下,JVM实现都应当提供调节JVM栈初始内存空间大小的手段;
&&&&&&HotSpot VM通过&-Xss&参数设置JVM栈内存空间大小;
5、异常情况
&&&&&& JVM规范中对该区域,规定了两种可能的异常状况:
(A)、StackOverflowError
&&&&&& 如果线程请求分配的栈深度超过JVM栈允许的最大深度时,JVM将会抛出一个StackOverflowError异常;
(B)、 OutOfMemoryError
&&&&&& 如果JVM栈可以动态扩展,当然扩展的动作目前无法申请到足够的内存去完成扩展,或者在建立新的线程时没有足够的内存去创建对应的虚拟机栈,那JVM将会抛出一个OutOfMemoryError异常;
该区域与方法执行的JVM字节码指令密切相关,这里篇幅有限,以后有时间会分析方法的调用与执行过程,再来详细介绍该区域。
2-3、本地方法栈
&&&&&& 本地方法栈(Native Method Stack)与 Java虚拟机栈类似;
&1、与Java虚拟机栈的区别
&&&&&& Java虚拟机栈为JVM执行Java方法(也就是字节码)服务;
&&&&&& 本地方法栈则为Native方法(指使用Java以外的其他语言编写的方法)服务;
2、HotSpot VM实现方式
&&&&&& JVM规范中没有规定本地方法栈中方法使用的语言、方式和数据结构,JVM可以自由实现;
&&&&&&HotSpot VM直接把本地方法栈和Java虚拟机栈合并为一个;
2-4、Java堆
&&&&&& Java堆(Java Heap)指常说的堆内存(Heap);
1、生存特点
&&&&&& 所有线程共享;
&&&&&& 生命周期与JVM相同;
&&&&&&为&new&创建的实例对象提供存储空间;
&&&&&& 里面存储的这些对象实例都是通过垃圾收集器(Garbage Collector)进行自动管理,所以Java堆也称&GC堆&(Garbage Collected Heap);
对GC堆以及GC的参数设置调整,就是JVM调优的主要内容;
3、存储内容
&&&&&&用于存放几乎所有对象实例;
&&&&&& (随JIT编译技术和逃逸分析技术发展,少量对象实例可能在栈上分配,详见后面介绍JIT编译的文章);
4、内存分配特点
(A)、Java堆划分
&&&&&& 为更好回收内存,或更快分配内存,需要对Java堆进行划分:
(I)、从垃圾收集器的角度来看
&&&&&& JVM规范没有规定JVM如何实现垃圾收集器;
&&&&&& 由于很多JVM采用分代收集算法,所以Java堆还可以细分为:新生代、老年代和永久代;
(II)、从内存分配角度来看
&&&&&& 为解决分配内存线程不安全问题,需要同步处理;
&&&&&& Java堆可能划分出每个线程私有的分配缓冲区(Thread Local Allocation Buffer,TLAB),减少线程同步;
HotSpot VM通过&-XX:+/-UseTLAB&指定是否使用TLAB;
(B)、分配调整
&&&&&& 和JVM栈一样,Java堆所使用的物理内存不需要保证是连续的,逻辑连续即可;
&&&&&& JVM规范允许Java堆被实现成固定大小的或者是根据计算动态扩展和收缩的:
&&&&&& 两种情况下,JVM实现都应当提供调节JJava堆初始内存空间大小的手段;
&&&&&& 在动态扩展或收缩的情况下,还应该提供调节最大和最小内存空间的手段;
(C)、HotSpot VM相关调整
&&&&&& 目前主流的JVM都把Java堆实现成动态扩展的,如HotSpot VM:
(1)、初始空间大小
&&&&&& 通过&-Xms&或&-XX:InitialHeapSize&参数指定Java堆初始空间大小;
&&&&&& 默认为1/64的物理内存空间;
(2)、最大空间大小
&&&&&& 通过&-Xmx&或&-XX:MaxHeapSize&参数指定ava堆内存分配池的最大空间大小;
&&&&&& 默认为1/4的物理内存空间;
&&&&&& Parallel垃圾收集器默认的最大堆大小是当小于等于192MB物理内存时,为物理内存的一半,否则为物理内存的四分之一;
(3)、各年代内存的占用空间与可用空间的比例
&&&&&& 通过&-XX:MinHeapFreeRatio&和&-XX:MaxHeapFreeRatio&参数设置堆中各年代内存的占用空间与可用空间的比例保持在特定范围内;
&&&&&& 默认:
&&&&&& &-XX:MinHeapFreeRatio=40&:即一个年代(新生代或老年代)内存空余小于40%时,JVM会从未分配的堆内存中分配给该年代,以保持该年代40%的空余内存,直到分配完&-Xmx&指定的堆内存最大限制;
&&&&&& &-XX:MaxHeapFreeRatio=70&:即一个年代(新生代或老年代)内存空余大于70%时,JVM会缩减该年代内存,以保持该年代70%的空余内存,直到缩减到&-Xms&指定的堆内存最小限制;
&&&&&&这两个参数不适用于Parallel垃圾收集器(通过“-XX:YoungGenerationSizeIncrement”、“-XX:TenuredGenerationSizeIncrement ”能及“-XX:AdaptiveSizeDecrementScaleFactor”调节);
(4)、年轻代与老年代的大小比例
&&&&&& 通过&-XX:NewRatio&:控制年轻代与老年代的大小比例;
&&&&&& 默认设置&-XX:NewRatio=2&表新生代和老年代之间的比例为1:2;
&&&&&& 换句话说,eden和survivor空间组合的年轻代大小将是总堆大小的三分之一;
(5)、年轻代空间大小
&&&&&& 通过&-Xmn&参数指定年轻代(nursery)的堆的初始和最大大小;
&&&&&& 或通过&-XX:NewSize&和&-XX:MaxNewSize&限制年轻代的最小大小和最大大小;
(6)、定永久代空间大小
&&&&&& 通过&-XX:MaxPermSize(JDK7)&或&-XX:MaxMetaspaceSize(JDK8)&参数指定永久代的最大内存大小;
&&&&&& 通过&-XX:PermSize(JDK7)&或&-XX:MetaspaceSize(JDK8)&参数指定永久代的内存阈值--超过将触发垃圾回收;
&&&&&& 注:JDK8中永久代已被删除,类元数据存储空间在本地内存中分配;
&&&&&& 详情请参考:
(D)、调整策略
&&&&&&关于这些参数的调整需要垃圾收集的一些知识(以后文章会介绍),先来简单了解:
&&&&&&当使用某种并行垃圾收集器时,应该指定期望的具体行为而不是指定堆的大小;
&&&&&&让垃圾收集器自动地、动态的调整堆的大小来满足期望的行为;
&&&&&&调整的一般规则:
&&&&&&除非你的应用程序无法接受长时间的暂停,否则你可以将堆调的尽可能大一些;
&&&&&&除非你发现问题的原因在于老年代的垃圾收集或应用程序暂停次数过多,否则你应该将堆的较大部分分给年轻代;
关于HotSpot虚拟机堆内存分代说明以及空间大小说明请参考:
5、异常情况
&&&&&&如果实际所需的堆超过了垃圾收集器能提供的最大容量,那Java虚拟机将会抛出一个OutOfMemoryError异常;
该部分的内存如何分配、垃圾如何收集,上面这些参数如何调整,将在以后的文章详细说明。
2-5、方法区
&&&&&&方法区(Method Area)是堆的逻辑组成部分,但有一个别名&Non-Heap&(非堆)用以区分;
1、生存特点
&&&&&&所有线程共享;
&&&&&&生命周期与JVM相同;
&&&&&&为类加载器加载Class文件并解析后的类结构信息提供存储空间;
&&&&&&以及提供JVM运行时常量存储的空间;
3、存储内容
&&&&&&用于存储JVM加载的每一个类的结构信息,主要包括:
(A)、运行时常量池(Runtime Constant Pool)、字段和方法数据;
(B)、构造函数、普通方法的字节码内容以及JIT编译后的代码;
(C)、还包括一些在类、实例、接口初始化时用到的特殊方法;
4、内存分配特点
(A)、分配调整
&&&&&&和Java堆一样,所使用的物理内存不需要保证是连续的;
&&&&&&或以实现成固定大小的或者是根据计算动态扩展和收缩的;
(B)、方法区的实现与垃圾回收
&&&&&&JVM规范规定:
&&&&&&虽然方法区是堆的逻辑组成部分,但不限定实现方法区的内存位置;
&&&&&&甚至简单的虚拟机实现可以选择在这个区域不实现垃圾收集;
&&&&&&因为垃圾收集主要针对常量池和类型卸载,效果不佳;
&&&&&&但方法区实现垃圾回收是必要的,否则容易引起内存溢出问题;
(C)、HotSpot VM相关调整
(I)、在JDK7中
&&&&&&使用永久代(Permanent Generation)实现方法区,这样就可以不用专门实现方法区的内存管理,但这容易引起内存溢出问题;
&&&&&&有规划放弃永久代而改用Native Memory来实现方法区;
&&&&&&不再在Java堆的永久代中生成中分配字符串常量池,而是在Java堆其他的主要部分(年轻代和老年代)中分配;
&&&&&&更多请参考:
(II)、在JDK8中
&&&&&&永久代已被删除,类元数据(Class Metadata)存储空间在本地内存中分配,并用显式管理元数据的空间:
&&&&&&从OS请求空间,然后分成块;
&&&&&&类加载器从它的块中分配元数据的空间(一个块被绑定到一个特定的类加载器);
&&&&&&当为类加载器卸载类时,它的块被回收再使用或返回到操作系统;
&&&&&&元数据使用由mmap分配的空间,而不是由malloc分配的空间;
&&&&&&通过&-XX:MaxMetaspaceSize& (JDK8)参数指定类元数据区的最大内存大小;
&&&&&&通过&-XX:MetaspaceSize& (JDK8)参数指定类元数据区的内存阈值--超过将触发垃圾回收;
&&&&&&详情请参考:
5、异常情况
&&&&&&如果方法区的内存空间不能满足内存分配请求,那Java虚拟机将抛出一个OutOfMemoryError异常;
2-6、运行常量池
&&&&&&运行常量池(Runtime Constant Pool)是方法区的一部分;
1、存储内容
&&&&&&是每一个类或接口的常量池(Constant_Pool)的运行时表示形式;
&&&&&&包括了若干种不同的常量:
&&&&&&(A)、从编译期可知的字面量和符号引用,也即Class文件结构中的常量池;
&&&&&&(B)、必须运行期解析后才能获得的方法或字段的直接引用;
&&&&&&(C)、还包括运行时可能创建的新常量(如JDK1.6中的String类intern()方法)
2-7、直接内存
&&&&&&直接内存(Direct Memory)不是JVM运行时数据区,也不是JVM规范中定义的内存区域;
&&&&&&是使用Native函数库直接分配的堆外内存;
&&&&&&被频繁使用,且容易出现OutOfMemoryError异常;
&&&&&&因为避免了在Java堆中来回复制数据,能在一些场景中显著提高性能;
3、实现方式
&&&&&&JDK1.4中新加入NIO(New Input/Output)类,引入了一种基于通道(Channel)与缓冲区(Buffer)的I/O方式;
&&&&&&它可以使用Native函数库直接分配堆外内存,然后通过一个存储在Java椎中的DirectByteBuffer对象作为这块内存的引用进行操作;
4、HotSpot VM相关调整
&&&&&&可以通过&-XX:MaxDirectMemorySize&参数指定直接内存最大空间;
&&&&&&不会受到Java堆大小的限制,即&-Xmx&参数限制的空间不包括直接内存;
&&&&&&这容易导致各个内存区域总和大于物理内存限制,出现OutOfMemoryError异常;
&&&&&&到这里,我们大体了解Java各内存区域是什么,有些什么特点了,但方法执行的JVM字节码指令如何在Java虚拟栈中运作的,以及Java堆内存如何分配、垃圾如何收集,如何进行JVM调优,将在以后的文章详细说明。
&&&&&&后面我们将分别去了解:方法的调用与执行、JIT编译--在运行时把Class文件字节码编译成本地机器码的过程、以及JVM垃圾收集相关内容……
【参考资料】
1、《The Java Virtual Machine Specification》Java SE 8 Edition:
2、《Java Platform, Standard Edition HotSpot Virtual Machine Garbage Collection Tuning Guide》:
3、《Memory Management in the Java HotSpot(TM) Virtual Machine》:
4、HotSpot虚拟机参数官方说明:
5、《深入理解Java虚拟机:JVM高级特性与最佳实践》第二版 第2章
&&相关文章推荐
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:134742次
积分:1848
积分:1848
排名:千里之外
原创:52篇
评论:14条
(4)(2)(7)(11)(7)(7)(14)
(window.slotbydup = window.slotbydup || []).push({
id: '4740887',
container: s,
size: '250,250',
display: 'inlay-fix'

我要回帖

更多关于 监控jvm内存使用情况 的文章

 

随机推荐