首页 > 代码库 > 关于java虚拟机内存管理的一些讲解

关于java虚拟机内存管理的一些讲解

java数据类型:

1)原始类型:Primitive Types(原始值)

数值类型(Numeric Types)
    整型类型(Integral Types),浮点类型(Floating-Point Types)

布尔类型(Boolean Types)

returnAddress类型:表示一条字节码指令的操作码(Opcode)。在所有的虚拟机支持的原始类型之中,只有 returnAddress 类型是不能直接 Java 语言的数据类型对应起来的。

2)引用类型:Reference Types(引用值)

类类型(Class Types)
数组类型(Array Types)
接口类型(Interface Types)

运行时数据区:

PC(Program Counter)寄存器:
    多线程环境下,每条线程拥有自己的PC寄存器,保存java虚拟机正在执行的字节码指令地址

java虚拟机栈(Stack):
    与线程同时创建,用于存储局部变量。基本数据类型,存引用。
    栈的优势是,存取速度比堆要快,仅次于直接位于CPU中的寄存器。
        1)如果线程请求分配的栈容量超过虚拟机允许的最大容量,则会抛出StackOverflowError异常(?怎么查看虚拟机栈的最大容量)
        2)如果java虚拟机栈可以动态扩展,但尝试的过程中无法申请到足够的内存去创建对应的虚拟机栈,则会抛出OutOfMemoryError异常

java堆(Heap):
    虚拟机启动时创建,可供各条线程共享的运行时内存区域,也是供所有类实例(值)和数组对象(值)内存分配的区域。
    只要是用new()来新建对象的,都会在堆中创建
    它存储了垃圾回收器(Garbage Collector)所管理的各种对象,这些对象无需也无法显式的销毁。虚拟机实现者可以根据实际需求来选择内存管理技术(垃圾回收技术)
    堆的容量可动态扩展,空间自动收缩,(?怎么初始化堆的大小)
        1)如果实际所需的堆超过了内存管理能提供的最大容量,那么虚拟机就会抛出OutOfMemoryError异常

方法区(Method Area):
    虚拟机启动时创建
    可供各条线程共享的运行时内存区域,存储了每个类的结构信息
        1)如果方法区的内存空间不能满足内存分配请求,那 Java 虚拟机将抛出一个OutOfMemoryError异常。

运行时常量池(Runtime Constant Pool):
    每一个运行时常量池都分配在 Java 虚拟机的方法区之中
    每个类和接口的常量池的运行时表示形式,编译时可知的数值字面量。在类和接口被加载到虚拟机后,对应的运行时常量池就被创建出来。
        1)创建类或接口的时候,如果构造运行时常量池所需要的内存空间超过了方法区所能提供的最大值,那 Java 虚拟机将会抛出一个 OutOfMemoryError 异常。
本地方法栈:
    使用到传统的栈(通常称之为“C Stacks”)来支持native方法(指使用 Java 以外的其他语言编写的方法)的执行,这个栈就是本地方法栈
        如果线程请求分配的栈容量超过本地方法栈允许的最大容量时,Java 虚拟机将会抛出一个StackOverflowError 异常。
        如果本地方法栈可以动态扩展,并且扩展的动作已经尝试过,但是目前无法申请到足够的内存去完成扩展,或者在建立新的线程时没有足够的内存去创建对应的本地方法栈,那 Java 虚拟机将会抛出一个 OutOfMemoryError 异常。

补充:

1)堆(Heap)和非堆(Non-heap)内存 
按照官方的说法:“Java 虚拟机具有一个堆,堆是运行时数据区域,所有类实例和数组的内存均从此处分配。堆是在 Java 虚拟机启动时创建的。”“在JVM中堆之外的内存称为非堆内存(Non-heap memory)”。可以看出JVM主要管理两种类型的内存:堆和非堆。简单来说堆就是Java代码可及的内存,是留给【开发人员】使用的;非堆就是JVM留给自己用的,所以方法区、JVM内部处理或优化所需的内存(如JIT编译后的代码缓存)、每个类结构(如运行时常数池、字段和方法数据)以及方法和构造方法的代码都在非堆内存中。

2)堆内存分配 
JVM初始分配的内存由-Xms指定,默认是物理内存的1/64;JVM最大分配的内存由-Xmx指定,默认是物理内存的1/4。默认空余堆内存小于40%时,JVM就会增大堆直到-Xmx的最大限制;空余堆内存大于70% 时,JVM会减少堆直到-Xms的最小限制。因此服务器一般设置-Xms、-Xmx相等以避免在每次GC 后调整堆的大小。

3)非堆内存分配 
JVM使用-XX:PermSize设置非堆内存初始值,默认是物理内存的1/64;由XX:MaxPermSize设置最大非堆内存的大小,默认是物理内存的1/4。

4)JVM内存限制(最大值) 
首先JVM内存限制于实际的最大物理内存(废话!呵呵),假设物理内存无限大的话,JVM内存的最大值跟操作系统有很大的关系。简单的说就32位处理器虽然可控内存空间有4GB,但是具体的操作系统会给一个限制,这个限制一般是2GB-3GB(一般来说Windows系统下为1.5G-2G,Linux系统下为2G-3G),而64bit以上的处理器就不会有限制了。

实例:
Student student = new Student();
这行代码就创建了两块内存空间,第一个在栈中,名字叫student,它就相当于指针类型的变量,它并不存放学生的姓名、年龄等具体的数值,这些数值存放堆中

关于Stringstr = “abc"的内部工作。Java内部将此语句转化为以下几个步骤:
(1)先定义一个名为str的对String类的对象引用变量:String str;
(2)在【栈】中查找有没有存放值为"abc"的地址,如果没有,则开辟一个存放字面值为"abc"的地址,接着创建一个新的String类的对象o,并将o的字符串值指向这个地址,而且在栈中这个地址旁边记下这个引用的对象o。如果已经有了值为"abc"的地址,则查找对象o,并返回o的地址。

==========================================================================

import java.util.*;
public class Test{
    public static void main(String []args){
        int size=1;
        Student[] students=new Student[size];
        for(int i=0;i<size;i++){
            students[i]=new Student();
        }
        Runtime runtime=Runtime.getRuntime();
        System.out.println(runtime.totalMemory());//获取JVM 分配给程序的内存数量 size=1:16252928    size=1000000:494317568
        System.out.println(runtime.freeMemory());//获取当前可用的内存数量              15965128                 32584224
        System.out.println(runtime.maxMemory());//获取JVM 可以申请到的最大内存数量        259522560                518979584
    }
}
class Student{
    private List<String> list=new ArrayList<String>(100);
}

直接运行时会抛java.lang.OutOfMemoryError: Java heap space异常
如果使用

java -Xms256M -Xmx512M Test,则运行正常

说明:
-Xms :set initial Java heap size(设置JVM初始化堆内存大小)
默认是物理内存的1/64
-Xmx :set maximum Java heap size(设置JVM最大的堆内存大小)
默认值为物理内存的1/4
空余堆内存小于40%时,JVM就会增大堆直到-Xmx的最大限制;
空余堆内存大于70% 时,JVM会减少堆直到-Xms的最小限制。
因此服务器一般设置-Xms、-Xmx相等以避免在每次GC 后调整堆的大小。