几个重要的概念:
java进程 -- 当启动一个java程序时,操作系统为进程分配资源,包括内存空间等
jvm -- java进程中必须存在的部分,通俗的说,即启动java程序时,jvm也就启动了,java字节码运行在jvm的管理之中
native method -- java跨平台的同时也损失了一些性能,native method使得java可以调用别的语言实现的一些功能模块,如C/C++写的.dll/.so,这些方法的实现代码运行在JVM之 外,所以内存的管理由程序员自己负责,如果这部分程序有内存泄漏,也会造成java进程的内存一直增长。
动态链接库 -- linux 下形如 libx.so, windows下形如libx.dll
jvm内存管理模型
在jvm中内存可分为两大类,线程共享内存和线程私有内存。
- 线程共享内存包括:
- Method Area
- 方法区,这部分存储的是jvm加载的class的信息,常量,静态变量,即时编译器编译后的代码等
- Java Heap
- java堆,这部分区域是java的所有对象实例,数组等的所在
- Method Area
- 线程私有内存区:
- Program Counter Register
- 程序计数寄存器,每个线程有自己的技术寄存器,存储当前线程执行字节码的地址
- JVM Stack
- jvm栈区,每启动一个线程,jvm就为该线程分配一个栈区,线程调用方法时和方法返回时会进行入栈和出栈操作
- Native Stack
- 本地方法栈区,与jvm stack类似,不过此区域是为调用本地方法服务的
- Program Counter Register
JDK1.4引入了NIO,出现了一个新的内存概念,DirectMemory(直接内存),它并不是虚拟机运行时数据区的一部分,也不是虚拟机规范中定义的内存区域。
在NIO中,引入了一种基于通道(Channel)与缓冲区(Buffer)的I/O方式,他可以使用Native函数库直接分配对外内存,然后通过存储在java堆中的DirectByteBuffer对象作为这块内存的引用进行操作,这样在一些场合下避免了在java堆和Native堆中来回复制数据,从而提高性能。
Jvm的垃圾收集器GC主要运行于Java Heap中,根据具体的垃圾收集策略,回收堆中对象所占空间,具体的垃圾收集策略以后有时间再补充
而在Method Area中,GC也会回收一些没有用的Class信息,但判断一个类信息没用的条件非常苛刻。在一些框架中,例如Spring、Struts等,由于使用动态代理和反射机制,会有大量的类信息,如果Method Area的大小设置较低,会抛出OutOfMemoryError(内存溢出错误异常)。此区域大小设置见下文。
在栈区,当线程结束后,GC也会收集其所占用的栈区内存空间
上述内存区域的大小理论上都是可以在JVM启动时加入参数来设置的,具体是否有效要看具体实现
以最典型的sun的hotspot为例
-Xms20M 最小堆内存设置为20M,据说默认为物理内存的1/64
-Xmx40M 最大堆内存设置为40M,默认为物理内存的1/4
-Xss2M 设置栈区的大小,-Xoss理论上是可以设置本地方法栈的大小,但实际上是无效的,因为hotspot不区分JVM stack 和 Native Stack
-XX:PermSize=20M 设置方法区的初始大小,默认为物理内存的1/64
-XX:MaxPermSize=30M 设置方法区的最大值,默认为物理内存的1/4
还有的说法是,MaxPermSize缺省值和-server -client选项相关。
-server选项下默认MaxPermSize为64m
-client选项下默认MaxPermSize为32m
note:-server即运行服务器程序,jvm会进行一些优化;-client是以客户端程序运行,默认为-client
直接内存也会溢出,可以通过 -XX:MaxDirectMemorySize=10M 来设置,默认和-Xmx一样,具体参见book 《深入理解Java虚拟机》 周志明著