Java的内存 -JVM 内存管理
一.综述
如果你学过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 内存管理的更多相关文章
- java虚拟机学习-JVM内存管理:深入垃圾收集器与内存分配策略(4)
Java与C++之间有一堵由内存动态分配和垃圾收集技术所围成的高墙,墙外面的人想进去,墙里面的人却想出来. 概述: 说起垃圾收集(Garbage Collection,下文简称GC),大部分人都把这项 ...
- java虚拟机学习-JVM内存管理:深入Java内存区域与OOM(3)
概述 Java与C++之间有一堵由内存动态分配和垃圾收集技术所围成的高墙,墙外面的人想进去,墙里面的人却想出来. 对于从事C.C++程序开发的开发人员来说,在内存管理领域,他们即是拥有最高权力的皇帝又 ...
- Java虚拟机:JVM内存分代策略
版权声明:本文为博主原创文章,转载请注明出处,欢迎交流学习! Java虚拟机根据对象存活的周期不同,把堆内存划分为几块,一般分为新生代.老年代和永久代(对HotSpot虚拟机而言),这就是JVM的内存 ...
- 【java虚拟机】jvm内存模型
作者:pengjunlee原文链接:https://blog.csdn.net/pengjunlee/article/details/71909239 目录 一.运行时数据区域 1.程序计数器 2.J ...
- 深入理解Java虚拟机之JVM内存布局篇
内存布局**** JVM内存布局规定了Java在运行过程中内存申请.分配.管理的策略,保证了JVM的稳定高效运行.不同的JVM对于内存的划分方式和管理机制存在部分差异.结合JVM虚拟机规范,一起来 ...
- JAVA系列之JVM内存调优
一.前提 JVM性能调优牵扯到各方面的取舍与平衡,往往是牵一发而动全身,需要全盘考虑各方面的影响.在优化时候,切勿凭感觉或经验主义进行调整,而是需要通过系统运行的客观数据指标,不断找到最优解.同时,在 ...
- java 深入理解jvm内存模型 jvm学习笔记
jvm内存模型 这是java堆和方法区内存模型 参考:https://www.cnblogs.com/honey01/p/9475726.html Java 中的堆也是 GC 收集垃圾的主要区域.GC ...
- 深入理解Java虚拟机(一)——JVM内存模型
文章目录 程序计数器 定义 作用 特点 Java虚拟机栈 定义 特点 本地方法栈 定义 Java堆 定义 特点 方法区 定义 特点 运行常量池 直接内存 总结 Java虚拟机的内存空间分为五个部分: ...
- tomcat增加内存 JVM内存调优
tomcat总是卡死,查看日志catalina.out 发现疯狂报错 如下,提示内存溢出 java.lang.OutOfMemoryError: Java heap space 此外常见的内存溢出有以 ...
随机推荐
- Android切换横竖屏不销毁前台Activity,也不影响后台Activity
在切换屏幕方向的时候,Activity默认会走销毁->重建的生命周期,而有时候我们不希望如此,就需要做些额外的设置了: 1.在AndroidMainifest.xml中对应的Activity标签 ...
- 剑指offer例题分享--6
前言:继续整理例题,快速做完这部分,然后继续用C++去刷数据结构和算法的题. 面试题28: 代码如下: #include<iostream> #include<stdio.h> ...
- HP-Socket v3.2.2
==========================================================================================v3.2.2 upg ...
- Python快速学习10: 循环的对象及设计 (生活的规律)
前言 系列文章:[传送门] 生活逐渐规律,按时睡觉.今天写博客,明天补时间看会书.慢慢的时间很珍惜 我很喜欢! 时钟就像个循环体,我们将它融入生活. 正文 循环对象的并不是随着Python的诞生就存在 ...
- ①泡茶看数据结构-表ADT
前言 小朽,晚上回到寝室.烧了开水,又泡了一杯下午喝了的小毛尖.耳机听着萨克斯,总结下今天学的数据结构和算法中的表ADT. 表ADT节点: #单链表 #双链表 #循环链表 ...
- 面试必备技能-HiveSQL优化
Hive SQL基本上适用大数据领域离线数据处理的大部分场景.Hive SQL的优化也是我们必须掌握的技能,而且,面试一定会问.那么,我希望面试者能答出其中的80%优化点,在这个问题上才算过关. Hi ...
- ES6躬行记(3)——解构
解构(destructuring)是一种赋值语法,可从数组中提取元素或从对象中提取属性,将其值赋给对应的变量或另一个对象的属性.解构地目的是简化提取数据的过程,增强代码的可读性.有两种解构语法,分别是 ...
- 翻译:window function(已提交到MariaDB官方手册)
本文为mariadb官方手册:window functions的译文. 原文:https://mariadb.com/kb/en/window-functions-overview/ 我提交到Mari ...
- 基于 LWIP 建立 TCP Server 与主机通信实验
LWIP 版本:2.0.3 上一篇文章是写如何将 LWIP 移植到板子上,今天晚上记录基于 LWIP 实现与主机的网络通信. 先是打开了原子的实验例程,大概浏览了一遍,觉得 TCP 网络网络通信也就是 ...
- 伪指令 ENTRY 与 END
ENTRY ENTRY 是程序入口伪指令.在一个完整的汇编程序中至少有一个 ENTRY,编译程序在编译连接时依据程序入口进行连接.在只有一个入口时,编译程序会把这个入口的地址定义为系统复位后的程序起始 ...