对于Java程序员,在虚拟机自动内存管理机制的帮助下,不需要再为每一个操作写配对的释放资源操作,不容易出现内存泄露和内存溢出问题。加深对Java虚拟机的理解,有助于在发现问题时精准定位问题,排查错误因素,写出更健壮的代码。
一、虚拟机内存
Java虚拟机在执行Java程序时,会把它所管理的内存划分成若干个不同的数据区域。这些区域都有各自的用途,以及创建和销毁的时间。从粗略的角度来说,可以分为堆和栈。堆为线程公有,栈为线程私有。
1.1 程序计数器
程序计数器是一块较小的内存空间,它可以看做是当前线程所执行的字节码的行号指示器。字节码解释器工作时,就是通过改变这个计数器的值来选取下一条需要执行的字节码指令。
为了线程切换后能恢复到正确的执行位置,每个线程都需要有一个独立的程序计数器。各条线程之间的计数器互不影响、独立存储,称这类内存区域为“线程私有”的内存。
1.2 Java虚拟机栈、本地方法栈
Java虚拟机栈(Java Virtual Machine Stacks)也是线程私有的,它的生命周期与线程相同。虚拟机栈描述的是Java方法执行的内存模型:每个方法在执行的同时会创建一个栈帧(Stack Frame),用于存储局部变量表、操作数栈、动态链接、方法出口等信息,每一个方法从调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中入栈到出栈的过程。
1.3 堆、方法区
对于大多数应用来说,Java堆(Java Heap)是Java虚拟机所管理的内存中最大的一块。Java堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存:所有的对象实例以及数组都要在堆上分配。
方法区(别名非堆:Non-Heap)是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
1.4 运行时常量池
运行时常量池(Runtime Constant Pool)是方法区的一部分。——用于存放类加载后的编译信息
1.5 直接内存
2、对象
2.1 对象的创建
①运行时常量池-检查类是否已被加载
②为新生对象分配内存
指针碰撞 Bump the Pointer :假设Java堆中的内存是绝对规整的,内存区分为可用和已用两部分,中间用指针分隔,作为分界点的指示器。分配内存就是将指针向空闲区域移动与对象大小相等的距离。
空闲列表 Free List:Java堆中的内存并不规整,虚拟机维护一个列表,记录哪些内存块是可用的,在分配的时候从列表上找到一个足够大的空间划分给对象实例,并更新列表上的记录。
CAS/失败重试 Compare And Swap:保证分配内存的并发安全
TLAB:本地线程分配缓冲(Thread Local Allocation Buffer):每个线程在Java堆中预先分配一小块内存,哪个线程要分配内存,就先在TLAB上分配,只有当TLAB用完并分配新的TLAB时,才需要同步锁定
③对对象进行必要的设置
将对象信息存储在对象的对象头(Object Head)之中。
2.2 对象的内存布局
对象在内存中存储的布局可以分为3块区域:对象头(Header)、实例数据(Instance Data)、对齐填充(Padding)
①对象头:
Mark Word:存储对象自身的运行时数据,如哈希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等。
类型指针:对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例,若对象为一个Java数组,则对象头中还必须有一块用于记录数组长度的数据。
②实例数据
③补齐
虚拟机大小必须是8字节的整数倍,不规整的
3、运行测试
1、使用IDEA,设置虚拟机参数: -Xss参数,减小栈内存容量。

测试程序:
public class JavaVMStackSOF {
private int stackLength = 1;
private void stackLeak(){
stackLength ++;
stackLeak();
}
public static void main(String[] args) {
JavaVMStackSOF oom = new JavaVMStackSOF();
try{
oom.stackLeak();
}catch (Throwable e){
System.out.println("stack length:" + oom.stackLength);
throw e;
}
}
}
输出结果:
抛出StackOverflowError异常,异常时输出的堆栈深度相应缩小。
.png)
二、垃圾回收策略
1、引用
判断引用是否存在,用于判断对象是否存活。
在JDK1.2之后,Java对对象的引用概念进行扩充, 将引用分为强引用(Strong Reference)、软引用(Soft Reference)、弱引用(Weak Reference)、虚引用(Phantom
Reference)
强引用就是指在程序代码之中普遍存在的、类似于“Object obj = new Object()”这类的引用。只要强引用还存在,垃圾回收器就永远不会回收被引用的对象。
2、垃圾回收算法
①标记-清除算法
最基础的收集算法是“标记-清除”算法:分为“标记”和“清除”两个阶段:首先标记出所有需要回收的对象,在标记完成后统一回收所有被标记的对象。
主要不足:一是效率问题,标记和清除两个过程的效率都不高,二是空间问题,标记清除之后会产生大量不连续的内存碎片,空间碎片太多可能会导致在程序运行过程中,需要分配较大对象时,无法找到足够的连续内存而不得不提前触发另一次垃圾回收动作。
②复制算法
将可用内存划分为两部分,当一部分用完时,将这部分中所有还存活的对象复制到另一部分中,再将已使用过的内存空间一次清理掉。
③标记-整理算法
与标记-清除算法类似,但标记后的后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动。
④分代收集算法
当前商业虚拟机的垃圾回收都采用“分代收集”算法。这种算法并没有什么新的思想,只是根据对象存活周期的不同,将内存划分为新生代、老年代。根据不同年代的特点,采用不同的回收算法。
3、垃圾回收器
①Serial收集器:
②G1收集器
对象优先在Eden分配:
大多数情况下,对象在新生代Eden区中分配,当Eden区没有足够空间进行分配时,虚拟机将发起一次Minor GC。
大对象直接进入老年代:所谓大对象是指需要大量连续内存空间的Java对象,最典型的的大对象就是很长的字符串以及数组。
- Java内存 模型理解
概述 在正式讲Java内存模型之前,我们先了解一些物理计算机并发问题,然后一点点的引出Java内存模型的由来. 多任务处理在现在计算机操作系统中几乎是一项必备的功能.这不单是因为计算机计算能力强大,更 ...
- 从 CPU 讲起,深入理解 Java 内存模型!
Java 内存模型,许多人会错误地理解成 JVM 的内存模型.但实际上,这两者是完全不同的东西.Java 内存模型定义了 Java 语言如何与内存进行交互,具体地说是 Java 语言运行时的变量,如何 ...
- Java 内存区域和GC机制
目录 Java垃圾回收概况 Java内存区域 Java对象的访问方式 Java内存分配机制 Java GC机制 垃圾收集器 Java垃圾回收概况 Java GC(Garbage Collection, ...
- Java系列笔记(3) - Java 内存区域和GC机制
目录 Java垃圾回收概况 Java内存区域 Java对象的访问方式 Java内存分配机制 Java GC机制 垃圾收集器 Java垃圾回收概况 Java GC(Garbage Collection, ...
- 【转载】Java系列笔记(3) - Java 内存区域和GC机制
Java系列笔记(3) - Java 内存区域和GC机制 转载:原文地址http://www.cnblogs.com/zhguang/p/3257367.html 目录 Java垃圾回收概况 Java ...
- Java内存分配以及GC
转自http://www.cnblogs.com/hnrainll/archive/2013/11/06/3410042.html 写的太棒了,简单易懂 Java垃圾回收概况 Java GC(Gar ...
- Java 内存回收机制——GC机制
一.Java GC 概念说明 Java GC(Garbage Collection,垃圾收集,垃圾回收)机制,是Java与C++/C的主要区别之一,作为Java开发者,一般不需要专门编写内存回收和垃圾 ...
- 转载:Java 内存区域和GC机制
原文链接:http://www.cnblogs.com/hnrainll/archive/2013/11/06/3410042.html 目录 Java垃圾回收概况 Java内存区域 Java对象的访 ...
- Java内存区域划分和GC机制
Java 内存区域和GC机制 目录 Java垃圾回收概况 Java内存区域 Java对象的访问方式 Java内存分配机制 Java GC机制 垃圾收集器 Java垃圾回收概况 Java GC(Ga ...
随机推荐
- CentOS7虚拟机安装vmtools
直接开始: 在安装vmtools之前,需要先安装两个小部件,否则将安装失败. 下面是步骤: 1.切换为root模式,需要输入root密码,但是不显示. 命令为: su 2.安装gcc 命令为: yum ...
- SpringBoot内置生命周期事件详解 SpringBoot源码(十)
SpringBoot中文注释项目Github地址: https://github.com/yuanmabiji/spring-boot-2.1.0.RELEASE 本篇接 SpringBoot事件监听 ...
- sphinx的使用
1.下载地址 http://sphinxsearch.com/downloads/release/ 2.将其解压到D:\sphinx,并在D:\sphinx下新建目录data(用来存放索引文件)与lo ...
- NetCore项目实战篇02---全局异常处理
在 .netcore中可以自定义自己的异常类型,步骤如下: 1.自定义自己的异常类型UserOperationException 并继承自Exception public class UserOper ...
- 信息竞赛进阶指南--递归法求中缀表达式的值,O(n^2)(模板)
// 递归法求中缀表达式的值,O(n^2) int calc(int l, int r) { // 寻找未被任何括号包含的最后一个加减号 for (int i = r, j = 0; i >= ...
- P2024 食物链(种类并查集)
P2024 [NOI2001]食物链 题目描述 动物王国中有三类动物 A,B,C,这三类动物的食物链构成了有趣的环形.A 吃 B,B吃 C,C 吃 A. 现有 N 个动物,以 1 - N 编号.每个动 ...
- SpringBoot返回JSON日期格式问题
SpringBoot中默认返回的日期格式类似于这样: "birth": 1537407384500 或者是这样: "createTime": "201 ...
- jQuery简单竖排手风琴折叠菜单代码
项目需求1.刚开始只显示,每个标题, 2.让每个 li列表隔行换色 3.当我点击某个标题时,下面的列表会缓慢的展开,其他列表展开的内容会收起 <!DOCTYPE html> <htm ...
- E. Yet Another Task with Queens(分类思想)
\(\color{Red}{描述}\) \(在n*n的棋盘上有m个K皇后,每个皇后可能被来自8个方向的其他皇后攻击\) \(每个皇后只可能被(0-8)只皇后攻击,分别求出被(0-8)只皇后攻击的皇后数 ...
- Git 获取远程仓库指定分支内容
1. 在本地一个空的文件夹中 git init (生成本地仓库) 2. 在刚刚的文件夹中随便建立一个文件 ,git add . (为了生成分支)(提交到暂存区) 3. git commit -m'1 ...