一.综述

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

而对java程序员来说,则没有这样的烦恼,因为java直接将内存管理交由jvm来管理,这样程序员在编写程序的时候就不用担心内存的使用情况而可以专注内容的实现。但这其实也造成了一点隐患,如果你不了解jvm内存管理的机制,很可能会因一些错误的代码写法而导致内存泄漏或内存溢出。

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

二.jvm内存结构

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

1.程序计数器

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

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

2.虚拟机栈

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

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

3.堆

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

java堆可以处于物理上不连续的内存空间中,只需要其是逻辑上连续的即可,如我们的磁盘空间。当在堆中无法申请到足够的内存空间时,会抛出OutOfMemoryError。

在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计算相关的变量被修改了,这也有可能导致内存泄漏,因为这时候这个对应基本已经不可达了。

Java的内存 -JVM 内存管理的更多相关文章

  1. java虚拟机学习-JVM内存管理:深入垃圾收集器与内存分配策略(4)

    Java与C++之间有一堵由内存动态分配和垃圾收集技术所围成的高墙,墙外面的人想进去,墙里面的人却想出来. 概述: 说起垃圾收集(Garbage Collection,下文简称GC),大部分人都把这项 ...

  2. java虚拟机学习-JVM内存管理:深入Java内存区域与OOM(3)

    概述 Java与C++之间有一堵由内存动态分配和垃圾收集技术所围成的高墙,墙外面的人想进去,墙里面的人却想出来. 对于从事C.C++程序开发的开发人员来说,在内存管理领域,他们即是拥有最高权力的皇帝又 ...

  3. Java虚拟机:JVM内存分代策略

    版权声明:本文为博主原创文章,转载请注明出处,欢迎交流学习! Java虚拟机根据对象存活的周期不同,把堆内存划分为几块,一般分为新生代.老年代和永久代(对HotSpot虚拟机而言),这就是JVM的内存 ...

  4. 【java虚拟机】jvm内存模型

    作者:pengjunlee原文链接:https://blog.csdn.net/pengjunlee/article/details/71909239 目录 一.运行时数据区域 1.程序计数器 2.J ...

  5. 深入理解Java虚拟机之JVM内存布局篇

    内存布局**** ​ JVM内存布局规定了Java在运行过程中内存申请.分配.管理的策略,保证了JVM的稳定高效运行.不同的JVM对于内存的划分方式和管理机制存在部分差异.结合JVM虚拟机规范,一起来 ...

  6. JAVA系列之JVM内存调优

    一.前提 JVM性能调优牵扯到各方面的取舍与平衡,往往是牵一发而动全身,需要全盘考虑各方面的影响.在优化时候,切勿凭感觉或经验主义进行调整,而是需要通过系统运行的客观数据指标,不断找到最优解.同时,在 ...

  7. java 深入理解jvm内存模型 jvm学习笔记

    jvm内存模型 这是java堆和方法区内存模型 参考:https://www.cnblogs.com/honey01/p/9475726.html Java 中的堆也是 GC 收集垃圾的主要区域.GC ...

  8. 深入理解Java虚拟机(一)——JVM内存模型

    文章目录 程序计数器 定义 作用 特点 Java虚拟机栈 定义 特点 本地方法栈 定义 Java堆 定义 特点 方法区 定义 特点 运行常量池 直接内存 总结 Java虚拟机的内存空间分为五个部分: ...

  9. tomcat增加内存 JVM内存调优

    tomcat总是卡死,查看日志catalina.out 发现疯狂报错 如下,提示内存溢出 java.lang.OutOfMemoryError: Java heap space 此外常见的内存溢出有以 ...

随机推荐

  1. 关于Flutter初始化流程,我必须告诉你的是...

    1. 引言 最近在做性能优化的时候发现,在混合栈开发中,第一次启动Flutter页面的耗时总会是第二次启动Flutter页面耗时的两倍左右,这样给人感觉很不好.分析发现第一次启动Flutter页面会做 ...

  2. python使用协程并发

    协程 协程是一种用户态的轻量级线程,又称微线程. 协程拥有自己的寄存器上下文和栈,调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈.因此:协程能保留上一次调 ...

  3. Perl中的自增、自减

    自增和自减 perl也支持数值类型的自增和自减操作.不仅如此,还支持字符.字符串的自增.自减. 如果自增(++)和自减(--)符号放在数值的前面,则先增减,再返回: 如果自增(++)和自减(--)符号 ...

  4. go基础系列:结构struct

    Go语言不是一门面向对象的语言,没有对象和继承,也没有面向对象的多态.重写相关特性. Go所拥有的是数据结构,它可以关联方法.Go也支持简单但高效的组合(Composition),请搜索面向对象和组合 ...

  5. spring-cloud-starter-gateway

    ********************************************************** Spring MVC found on classpath, which is i ...

  6. 数据分析面试题之Pandas中的groupby

      昨天晚上,笔者有幸参加了一场面试,有一个环节就是现场编程!题目如下:   示例数据如下,求每名学生(ID)对应的成绩(score)最高的那门科目(class)与ID,用Python实现: 这个题目 ...

  7. JS脚本获取URL参数并调用

    首先增加一个脚本库,可以是Zepto或者jQuery的,然后获取之后使用switch进行分流处理 <script type="text/javascript" src=&qu ...

  8. 结构型---组合模式(Composite Pattern)

    组合模式的定义 组合模式允许你将对象组合成树形结构来表现”部分-整体“的层次结构,使得客户以一致的方式处理单个对象以及对象的组合. 组合模式实现的最关键的地方是——简单对象和复合对象必须实现相同的接口 ...

  9. [PHP] 算法-字符串的左循环的PHP实现

    汇编语言中有一种移位指令叫做循环左移(ROL),现在有个简单的任务,就是用字符串模拟这个指令的运算结果.对于一个给定的字符序列S,请你把其循环左移K位后的序列输出.例如,字符序列S=”abcXYZde ...

  10. Java 文件流操作.

    一.概念 在Java中,文件的输入和输出是通过流(Stream)来实现的.一个流,必有源端和目的端,它们可以是计算机内存的某些区域,也可以是磁盘文件,甚至可以是 Internet 上的某个 URL.对 ...