内存区域

  Java虚拟机在执行Java程序的过程当中都会把所管理的内存划分为若干个不一样的区域,下面就是几个运行时的数据区域:
java

咱们逐个来看看各自的领域:多线程

 

1.程序计数器spa

  程序计数器就是一块较小的内存空间,它的做用能够看做是当前线程所执行的字节码的行号指示器,说白就就是经过这个计数器的值来选取下一条须要执行的字节码指令,就是记录程序行走的步骤,什么循环、跳转、异常处理都是依赖这个计数器完成的。就拿个例子来讲说,咱们的Java的多线程是经过线程以前的轮流切换来实现的,一个线程中止后,另一个线程启动,那么回来的时候怎么知道本身执行到哪里呢,就用这个程序计数器来记录了。命令行

 

2.Java虚拟机栈线程

  虚拟机栈描述的是Java方法执行的内存模型:每一个方法被执行的时候都会同时建立一个栈帧用于存储局部变量表、操做数栈、动态连接、方法出口等信息,每个方法被调用直至执行完成的过程,就对应着一个栈帧入栈到出栈的过程。指针

  其实不少人把Java内存区分为堆和栈,这种分法不许确,内部的分法远比这复杂,这里须要指出的是,咱们说的Java栈其实就是这里的虚拟机栈了。code

 

3.本地方法栈对象

  本地方法栈与虚拟机栈的做用很是的类似,区别是虚拟机栈为虚拟机执行Java方法服务,本地方法栈为虚拟机使用到的Native方法服务。blog

 

4.Java堆接口

  堆是Java虚拟机所管理的内存中最大的一块,Java堆是被全部线程共享的一块内存区域,在虚拟机启动时建立,这个内存区域的惟一目的就是存放对象实例,几乎全部对象的实例都是在这里分配内存的。
  堆其实也是垃圾收集器管理的主要区域,不少时候也被称为“GC堆”。

 

5.方法区:

  方法区跟堆是同样的,是全部线程共享的内存区域,它用于存储已经被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码。

 

6.运行时常量池

  这个运行时常量池也是方法区的一部分,Class文件中除了有类的版本、ziduan 、方法、接口等描述信息外,还有一项信息是常量池,用于放编译期生成的各类字面量和符号引用,这部份内容将在类加载后存放在方法去的运行时常量池中。

 

咱们来看看对象访问是如何进行的?

很简单的一句:Object object = new Object();执行了这句话后内存这样执行的:“Object object”这部分产生的结果就是在Java栈的本地变量表上,有一个reference类型数据的出现,而“new Object()”这部分的结果就是在堆上造成一块存储了Object类型的内存,另外,在堆上还有能查到此对象类型数据(父类、实现的接口、方法)的地址信息,这个东西真正存储在方法区中。

不一样的虚拟机实现对象的访问有所不一样,下面介绍这两种。

1.句柄访问方式

  Java堆中将会划分出一块内存来作句柄池,存储的就是对象的句柄地址,包含对象实例数据和类型数据的具体地址信息。

2.直接指针访问方式

  就在堆中生成一块内存区,里面既有类型数据的指针也有对象实例数据。

 

咱们来看看JVM中溢出的各类状况吧,了解了解:

1.Java堆溢出

  java堆是用于存储对象的,若是咱们不断的建立对象的话,在对象数大于对的最大容量的时候,就会发生溢出。

 

public class HeapOOM {
    static class OOMObject {
        
    }
    
    public static void main(String[] args) {
        List<OOMObject> list = new ArrayList<OOMObject>();
        while(true) {
            list.add(new OOMObject());
        }
    }
}

 

这样的话咱们就能够在命令行下看到这样的状况:

 

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space

 

这个就是堆溢出的标志了。

 

2.虚拟机栈和本地方法栈溢出

  栈说白就是跟方法的调用有关,若是咱们不断的调用方法,局部变量多,栈的容量就很快会满。

 

public class StackTest {
    
    public void method() {
        method();
    }
    
    public static void main(String[] args) {
        StackTest stack = new StackTest();
        try {
            stack.method();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

 

你运行下看看就会出现下面的错误了:

 

Exception in thread "main" java.lang.StackOverflowError

 

哈哈,这个就是栈的溢出,请记住它。

 

3.运行时常量池溢出

  要想看到这个溢出就要不停的往运行时常量池添加东西,这里咱们要介绍一个方法:String.intern(),这个是Native方法,也就是本地的方法,用法:若是池中包含一个等于此String对象的字符串,则返回此String对象,不然就将这个字符串添加到运行时常量池中,返回此String对象,下面咱们就用这个方法来让运行时常量池溢出。

 

public class RuntimeConstantPoolTest {
    public static void main(String[] args) {
        List<String> list = new ArrayList<String>();
        int i = 0;
        while(true) {
            list.add(String.valueOf(i++).intern());
        }
    }
}

 

而后就会出现下面的错误:

 

Exception in thread "main" java.lang.OutOfMemoryError: PermGen space

 

PermGen space的全称是Permanent Generation space,是指内存的永久保存区域,这块内存主要是被JVM存放Class和Meta信息的,也就是说这个运行时常量池是放在方法区的,看看方法区的定义就知道了。