Java的内存 -JVM 内存管理

一.综述

若是你学过C或者C++,那么你应该感觉过它们对内存那种强大的掌控力。可是强大的能力每每须要更强大的控制力才能保证能力不被滥用,若是滥用C/C++的内存管理那么很容易出现指针满天飞的状况,不出问题还好,一出问题debug起来简直让人头疼得不要不要的。借用一句话,“指针一时爽,重构火葬场”。java

而对java程序员来讲,则没有这样的烦恼,由于java直接将内存管理交由jvm来管理,这样程序员在编写程序的时候就不用担忧内存的使用状况而能够专一内容的实现。但这其实也形成了一点隐患,若是你不了解jvm内存管理的机制,极可能会因一些错误的代码写法而致使内存泄漏或内存溢出。程序员

注:所述内容取自Jdk1.6。多线程

二.jvm内存结构

三.每部分存储了哪些数据

1.程序计数器

程序计数器是一块较小的内存空间,能够看做当前线程所执行字节码的行号指示器,即指向正在执行的字节码。在概念模型中,字节码解释器的工做就是经过改变这个程序计数器的值来选取下一条字节码的指令。app

值得一提的是,由于java的多线程是经过线程轮流切换并分配处理器执行时间来实现的(即一个小的时间段内仍然只有一个线程处于运行状态),每一个线程的执行指令都不同,为了使线程切换后能正确执行到该线程的下一指令,每一个线程都须要一个独立的程序计数器,因此程序计数器使线程私有的。less

2.虚拟机栈

虚拟机堆和虚拟机栈能够说是jvm内存中最值得咱们关注的两块内存区域。虚拟机栈是内存私有的,每一个方法在执行的同时会建立一个栈帧。用于存储局部变量表,操做数栈,动态连接等信息。每个方法调用到执行完成的过程,其实就是对应一个栈帧在虚拟机栈中入栈到出栈的过程。jvm

在这个区域可能出现的异常状况有两种,分别是StackOverflowError和OutOfMemoryError。当栈动态拓展过深,好比无限递归时会出现StackOverflowError,而当没法申请到足够内存时,则发生OutOfMemoryError。ui

3.堆

对大部分应用来讲,堆是jvm管理的内存中最大的一块。与虚拟机栈不一样,堆是被全部线程共享的。它的做用是存放对象的实例,几乎全部的对象实例都在这里分配内存。
堆同时也是垃圾收集器管理的主要区域,从内存回收的角度看,java堆能够分为“新生代”和“老年带”。this

java堆能够处于物理上不连续的内存空间中,只须要其是逻辑上连续的便可,如咱们的磁盘空间。当在堆中没法申请到足够的内存空间时,会抛出OutOfMemoryError。spa

在JDK1.6以前,字符串常量池一直放在方法区中,可是到了jdk1.7的时候,常量池便被移出方法区,而转到Java堆中区了。线程

在HotSpot虚拟机里实现的字符串常量池功能的是一个StringTable类,它是一个Hash表。这个哈希表在每一个HotSpot虚拟机的实例只有一份,被全部的类共享。字符串常量由一个一个字符组成,而且相同字符串只保留一份。

HotSpot虚拟机的说明以下:

Area: HotSpot 
Synopsis: In JDK 7, interned strings are no longer allocated in the permanent generation of the Java heap, but are instead allocated in the main part of the Java heap (known as the young and old generations), along with the other objects created by the application. This change will result in more data residing in the main Java heap, and less data in the permanent generation, and thus may require heap sizes to be adjusted. Most applications will see only relatively small differences in heap usage due to this change, but larger applications that load many classes or make heavy use of the String.intern() method will see more significant differences. 
RFE: 6962931 

大意即是说JDK1.7中的字符串不会再分配到Java的永久代中,而是分配到Java堆中。这意味着更多的数据将存于堆中而更少的数据存于方法区,这致使堆大小须要调整以作适配。因为此更改,大多数应用程序只会看到堆使用中的相对较小的差别,较大型的应用可能会发现其显著差别

4.方法区

与java堆同样,方法区也是全部线程共享的。它主要的功能时存储虚拟机加载的类信息,常量,静态变量,编译后的代码数据等。能够明显发现,方法区存放的这些数据都是比较难以被回收的,因此这个区的垃圾回收行为较少发生。

若在方法区中没法申请到足够的内存时,将会抛出OutOfMemoryError。

另外方法区中有一个运行时常量池。注意这里不是字符串常量池,它存储的是类编译时期生成的各类字面量和符号引用,而且每一个类都有一个。

这里介绍一下什么是字面量和符号引用:

  • 字面量包括:1.文本字符串 2.八种基本类型的值 3.被声明为final的常量等;
  • 符号引用包括:1.类和方法的全限定名 2.字段的名称和描述符 3.方法的名称和描述符。

 

四.内存溢出和内存泄漏

内存溢出很好理解,就是发生OutOfMemoryError,好比当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()); } } }

因为无限循环不断新建对象,最终会致使内存溢出。

那么内存泄漏呢?

内存泄漏的缘由主要是一个对象已经再也不须要使用,但被另外一个长对象持有时,就有可能发生内存泄漏。好比在方法内一个对象被全局的HashMap持有,方法执行结束没有释放就会致使内存泄漏。

再有就是当一个对象被存储进HashSet后,其hashcode计算相关的变量被修改了,这也有可能致使内存泄漏,由于这时候这个对应基本已经不可达了。

相关文章
相关标签/搜索