对于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 ...
随机推荐
- Android Resourse
为什么80%的码农都做不了架构师?>>> 使用情景: 实现帧动画步骤的控制,这样动态的获取Drawable资源对应的R id,播放到那一步就加载到哪一步 private void ...
- 流畅的python读书笔记-第十章-继承优缺点
继承的优缺点 推出继承的初衷是让新手顺利使用只有专家才能设计出来的框架.--Alan Kay 子类化内置类型很麻烦 (如 list 或 dict)) ,别搞这种 直接子类化内置类型(如 dict.li ...
- CCF系列奖获奖名单公布,鲍虎军、周志华获CCF王选奖 | CNCC 2017
本文讲的是CCF系列奖获奖名单公布,鲍虎军.周志华获CCF王选奖 | CNCC 2017, 由中国计算机学会(CCF)主办,福州市人民政府.福州大学承办,福建师范大学.福建工程学院协办的2017中国计 ...
- 背英语单词很困难,不妨学习一下词根词缀吧(每天10个词根、词缀)Part 3
1.ir- 不,向内 例词: irregular=ir(不)-regular(规则的)=不规则的 irrigate=ir(向内)-rigate(浇水)=灌溉 2. kilo- 千 例词: kilogr ...
- 数学--数论--整除分块(巨TM详细,学不会,你来打我)
1.概念 从一道例题说起 在介绍整除分块之前,我们先来看一道算数题:已知正整数n,求∑i=1n⌊ni⌋已知正整数n,求∑i=1n⌊ni⌋在介绍整除分块之前,我们先来看一道算数题: 已知正整数n,求∑i ...
- redis 6.0下redis-cluster-proxy代理尝试
伴随着Redis6.0的发布,作为最令人怦然心动的特性之一,Redis官方同时推出Redis集群的proxy了:redis-cluster-proxy,https://github.com/Redis ...
- turtle库应用实例-五角星绘制
五角星绘制 ...
- 使用Codemirror打造Markdown编辑器
前几天突然想给自己的在线编译器加一个Markdown编辑功能,于是花了两三天敲敲打打初步实现了这个功能. 一个Markdown编辑器需要有如下常用功能: 粗体 斜体 中划线 标题 链接 图片 引用 代 ...
- Hadoop入门学习笔记-第二天 (HDFS:NodeName高可用集群配置)
说明:hdfs:nn单点故障,压力过大,内存受限,扩展受阻.hdfs ha :主备切换方式解决单点故障hdfs Federation联邦:解决鸭梨过大.支持水平扩展,每个nn分管一部分目录,所有nn共 ...
- Spring官网阅读(十七)Spring中的数据校验
文章目录 Java中的数据校验 Bean Validation(JSR 380) 使用示例 Spring对Bean Validation的支持 Spring中的Validator 接口定义 UML类图 ...