java虚拟机

简介: Java内存模型程序计数器内存中较小的内存空间,通过计数器的值可以选取下一条执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成。线程私有,生命周期跟线程相同。

Java内存模型
java虚拟机

程序计数器
内存中较小的内存空间,通过计数器的值可以选取下一条执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成。

线程私有,生命周期跟线程相同。

如果正在执行一个Native方法,那么这个计数器值将为空。

虚拟机栈
线程私有,生命周期跟线程相同。

每个方法在执行同时都会创建一个栈帧,用于存储局部变量表、操作数栈、动态链接、方法出口等信息。

在Java虚拟机规范中,对这个区域规定了两种异常情况:如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError异常;
如果虚拟机栈可以动态扩展,如果扩展时无法申请到足够的内存,就会抛出OutOfMemoryError异常。

本地方法栈
跟虚拟机栈所发挥的作用相似,区别在于虚拟机栈为虚拟机执行Java(也就是字节码)服务,而本地方法栈则为虚拟机使用到的Native方法服务。

Java堆
用于存放对象实例,是Java虚拟机所管理的内存中最大的一块,同时也是所有线程共享的一块内存区域。

因为Java堆是垃圾收集器管理的主要区域,因此很多时候也被称为“GC"堆。由于现在收集器基本都采用分代收集算法,所以Java堆还可以细分为

新生代
老年代
永久代(永久代是Hotspot虚拟机特有的概念,是方法区的一种实现,别的JVM都没有这个东西。在Java 8中,永久代被彻底移除,取而代之的是另一块与堆不相连的本地内存——元空间。)
当一个对象被创建时,它首先进入新生代,之后有可能被转移到老年代中。

新生代存放着大量的生命很短的对象,因此新生代在三个区域中垃圾回收的频率最高。为了更高效地进行垃圾回收,把新生代继续划分成以下三个空间:

Eden
From Survivor
To Survivor
方法区
与Java堆一样,各个线程共享的内存区域,存储已被虚拟机加载的类信息、常量、静态变量、即使编译器编译后的代码等数据。

运行时常量池
方法区的一部分,用于存放编译器生成的各种字面量和符号引用。

运行时常量池相对于class文件常量池的另外一个重要特征是具备动态性,Java语言并不要求常量一定只有编译期才能产生,也就是并非预置入class文件中常量池的内容才能进入方法区运行时常量池,运行期间也可能将新的常量放入池中,这种特性被开发人员利用得比较多的便是String类的intern()方法。

直接内存
在JDK1.4中新加入了NIO类,引入了一种基于通道与缓冲区的I/O方法,它可以使用Native函数库直接分配堆外内存,然后通过一个存储在Java堆中的DirectByteBuffer对象作为这块内存的引用进行操作。
堆外内存之 DirectByteBuffer详解

HotSpot虚拟机对象
对象的创建
在语言层上,创建对象通常仅仅是一个new关键字而已,而当虚拟机遇到一条new执行时,将由一下步骤:

检查类是否加载、解析、初始化过,没有则先执行相应的类加载过程。
在堆中分配内存

划分可用空间:

指针碰撞:堆内存规整
空闲列表:堆内存不规整
并发问题

同步:采用CAS配上失败重试的方式保证更新操作的原子性
把内存分配动作按照线程划分在不同的空间之中进行
将分配到的内存空间都初始化零值
设置对象的类实例、元数据、哈希码、GC分代年龄等信息。
执行<init>方法
对象的内存布局
对象在内存中储存的布局可以分为3块区域:

对象头

对象运行时数据、哈希码、GC分代年龄、锁状态标记、线程持有的锁、偏向线程ID等
类型执行:即对象执向它的类元数据的指针,指明对象数据哪个类的实例。
实例数据

对象真正存储的有效信息
对齐填充

占位符作用
对象的访问定位
句柄定位
直接指针
垃圾收集器
程序计数器、虚拟机栈和本地方法栈这三个区域属于线程私有的,只存在于线程的生命周期内,线程结束之后也会消失,因此不需要对这三个区域进行垃圾回收。垃圾回收主要是针对 Java 堆和方法区进行。

判断对象是否死亡
引用计数算法
给对象添加一个引用计数器,每当有一个地方引用它,计数器值就加1;引用时效时,计算器值就减1;当计数器值为0的对象就是不可能再被使用的。

当两个对象相互引用时,此时引用计数器的值永远不为0,导致无法对它们进行垃圾回收。

public class ReferenceCountingGC {
public Object instance = null;

    public static void testGC() {
        ReferenceCountingGC objA = new ReferenceCountingGC();
        ReferenceCountingGC objB = new ReferenceCountingGC();
        objA .instance = objB ;
        objB .instance = objA ;
        objA = null;
        objB = null;

        System.gc();
    }
}

可达性分析算法
以GC Roots为起始点,从这些节点开始向下搜索,能够搜索到的对象都是存活的,不可达的对象则为不可用。

java虚拟机

在Java语言中,可作为GC Roots的对象包括下面几种:

虚拟机栈中引用的对象
方法区中静态属性引用的对象
方法区中常量引用的对象
本地方法栈中Native方法引用的对象
引用类型
无论是引用计数算法还是可达性分析算法判断对象是否存活都与引用有关。在JDK1.2之后,Java对引用的概念进行了扩充,划分为强度不同的四个的引用类型。

强引用
通过new来创建对象的引用类型,被强引用的对象永远不会被垃圾收集器回收。

Object obj = new Object();
软引用
通过SortReference类来实现,只有在内存不足的时候才会被回收。

Object obj = new Object();
SoftReference<Object> sr = new SoftReference<Object>(obj);
obj = null;

弱引用
通过WeakReference类来实现,只能存活到下一次垃圾收集发生之前。

Object obj = new Object();
WeakReference<Object> wr = new WeakReference<Object>(obj);
obj = null;

WeakHashMap 的 Entry 继承自 WeakReference,主要用来实现缓存。

private static class Entry<K,V> extends WeakReference<Object> implements Map.Entry<K,V>
Tomcat 中的 ConcurrentCache 就使用了 WeakHashMap 来实现缓存功能。ConcurrentCache 采取的是分代缓存,经常使用的对象放入 eden 中,而不常用的对象放入 longterm。eden 使用 ConcurrentHashMap 实现,longterm 使用 WeakHashMap,保证了不常使用的对象容易被回收。

public final class ConcurrentCache<K, V> {

private final int size;

private final Map<K, V> eden;

private final Map<K, V> longterm;

public ConcurrentCache(int size) {
    this.size = size;
    this.eden = new ConcurrentHashMap<>(size);
    this.longterm = new WeakHashMap<>(size);
}

public V get(K k) {
    V v = this.eden.get(k);
    if (v == null) {
        v = this.longterm.get(k);
        if (v != null)
            this.eden.put(k, v);
    }
    return v;
}

public void put(K k, V v) {
    if (this.eden.size() >= size) {
        this.longterm.putAll(this.eden);
        this.eden.clear();
    }
    this.eden.put(k, v);
}

}
虚引用
也称为幽灵引用或者幻影引用,是最弱的一种引用关系。

通过PhantomReference类来实现,为一个对象设置虚引用关联的唯一目的就是能在这个对象被收集器回收时收到一个系统通知。

Object obj = new Object();
PhantomReference<Object> wr = new PhantomReference<Object>(obj, null);
obj = null;

垃圾收集算法
标记清除
算法分为“标记”跟“清除”两个阶段:首先标记出所有需要回收的对象,在标记完成之后统一回收所有被标记的对象。

不足:

效率问题,标记跟清除两个过程的效率都不高
空间问题,标记清除之后会产生大量不连续的内存碎片。
java虚拟机
复制算法
将内存分为大小相等的两块,每次只使用其中的一块,当这块内存用完了,就将还存活的对象负责到另一块上面,然后再把一是要难过过得内存空间一次清理掉。

不足:

代价太高,只使用一半内存。
java虚拟机

标记整理算法
首先标记出所有需要回收的对象,然后将所有存活的对象都向一端移动,最后清理掉端边界以外的内存。

java虚拟机

分代收集算法
根据对象的存活周期将内存划分为几块。一般将Java堆分为新生代跟老年代,这样就可以根据各个年代的特点采用最适当的收集算法。

新生代:复制算法
老年代:标记整理或标记清除算法。

目录
相关文章
|
1月前
|
存储 Java 数据安全/隐私保护
【JVM】Java虚拟机栈(Java Virtual Machine Stacks)
【JVM】Java虚拟机栈(Java Virtual Machine Stacks)
35 0
|
4月前
|
监控 安全 Java
Java基础知识:解释一下Java虚拟机(JVM)是什么。
Java基础知识:解释一下Java虚拟机(JVM)是什么。
39 3
|
25天前
|
缓存 Java C#
【JVM故障问题排查心得】「Java技术体系方向」Java虚拟机内存优化之虚拟机参数调优原理介绍(一)
【JVM故障问题排查心得】「Java技术体系方向」Java虚拟机内存优化之虚拟机参数调优原理介绍
68 0
|
3月前
|
Linux Windows
FinalShell连接Linux虚拟机报错java.net.ConnectException: Connection timed out: connect(亲测有效)
FinalShell连接Linux虚拟机报错java.net.ConnectException: Connection timed out: connect(亲测有效)
163 0
|
1月前
|
存储 Java 开发者
深入理解Java虚拟机(JVM)内存管理
【2月更文挑战第11天】 在本篇文章中,我们将深入探讨Java虚拟机(JVM)的内存管理机制,一项对于优化Java应用性能至关重要的技术领域。不同于常规的技术文章摘要,我们不仅概述了JVM内存管理的基本概念,还将引导读者通过实际案例理解其在现实世界应用中的重要性。从堆(Heap)和栈(Stack)的区别开始,到垃圾收集(Garbage Collection)机制的工作原理,本文旨在为Java开发者提供一个清晰、系统的JVM内存管理知识框架,帮助他们在开发过程中做出更加明智的决策。
|
2月前
|
存储 算法 Java
Java虚拟机内存管理机制
【2月更文挑战第7天】本文主要介绍了Java虚拟机内存管理机制的基本原理和实现方式。Java虚拟机的内存管理机制是Java程序运行的重要组成部分,对程序性能和稳定性有着直接的影响。文章首先从Java虚拟机内存模型入手,介绍了Java虚拟机中堆内存、方法区、栈、PC寄存器等内存区域的功能特点和使用方式;然后详细阐述了Java虚拟机内存管理机制的垃圾回收算法和回收器的分类、优化和实现过程;最后介绍了一些常见的内存问题和优化技巧,以及如何通过代码调优和合理使用内存配置参数来提高程序的性能和稳定性。
|
3月前
|
安全 前端开发 Java
【JVM】<Java虚拟机>JVM架构&各种**虚拟机
【1月更文挑战第26天】【JVM】<Java虚拟机>JVM架构&各种**虚拟机
|
3月前
|
自然语言处理 Oracle Java
【JVM】<Java虚拟机>JVM和JAVA体系结构
【1月更文挑战第26天】【JVM】<Java虚拟机>JVM和JAVA体系结构
|
3月前
|
监控 算法 Java
垃圾回收机制与性能调优:描述Java虚拟机(JVM)的垃圾回收算法,并解释为什么需要这些算法。如何使用Java内存分析工具(如VisualVM、JConsole或MAT)来识别和解决内存泄漏问题?
垃圾回收机制与性能调优:描述Java虚拟机(JVM)的垃圾回收算法,并解释为什么需要这些算法。如何使用Java内存分析工具(如VisualVM、JConsole或MAT)来识别和解决内存泄漏问题?
28 1
|
4月前
|
关系型数据库 MySQL Java
linux入门之虚拟机构建以及java环境搭建(1)
linux入门之虚拟机构建以及java环境搭建(1)