JVM详解

CPU寄存器 - 高速缓存 - 主存(RAM)。处理速度 从 左往右 速度 递减

JVM , JMM ,JDK ,JRE

JDK : Java Development Kit 【Java 软件开发工具包 (SDK)】

JRE : Java Runtime Environment

JMM : Java Memory Model

JVM ::Java Virtual Machine



JDK的安装目录下有一个jre目录,里面有两个文件夹bin和lib,bin里的可以认为就是jvm,lib中则是jvm工作所需要的类库,JVM + lib = JRE

1.JVM的内部结构

中间虚线的是 运行时数据区域 (是一种规范)


每个JVM都有两种机制:

①类装载子系统:装载具有适合名称的类或接口

②执行引擎:负责执行包含在已装载的类或接口中的指令 

内存空间 【组成】 :

JVM内存空间包含:方法区、java堆、java栈、本地方法栈。

方法区  是各个线程共享的区域,存放类信息、常量、静态变量。

java堆 也是线程共享的区域,存放类的实例化和数组的。java堆的空间是最大的。如果java堆空间不足了,程序会抛出OutOfMemoryError异常。

方法区 和 堆的区别 :

堆  存放的是 对象 和 数组 ;

方法区  存的是类信息,静态变量,常量,处理逻辑的指令集。

关系就是 : “方法区 相当于 类”  “堆 相当于 对象”

方法区 逻辑上  在 堆的 Permanent  (永久代 【非堆】)上。物理上 还是在堆上的

JDK1.7 之前 : 字符串常量池 在 方法区 (在堆的永久代)中

JDK1.7:字符串常量池 从 方法区 移到 堆

JDK1.8:字符串常量池 还在方法区 中,但是方法区的实现从 永久代(Perm)变成 元空间(MetaSpace)。

元空间: 本质上与永久代类似,都是JVM规范中方法区的实现。不过 元数据空间并不在虚拟机中,而是使用本地内存。

线程栈 是每个线程私有的区域,用于存储局部变量。它的生命周期线程相同,

本地方法栈 是 为虚拟机的Native方法服务的。

Native方法  :调用非java代码的接口。

使用Native Method的原因 : java应用需要与java外面的环境交互 【比如:与操作系统交互】。

区别:JVM线程栈为 虚拟机执行Java方法服务,而本地方法栈则是为虚拟机使用到的Native方法服务。

指令计数器,指向当前线程正在执行的字节码指令地址,行号。比如,切换线程后,线程要知道自己原来是在哪里运行的,然后接着运行。

执行引擎  当然就是根据PC寄存器调配的指令顺序,依次执行程序指令。

2.java 堆 的组成结构


Heap = {Old + NEW + Permanent = { Eden , Survivor0, Survivor1 ,Permanent } }

一块是 NEW Generation(新生代), 另一块是Old Generation(老年代). 在New Generation中,有一个叫Eden 的空间,主要是用来存放新生的对象,还有两个Survivor Spaces(Survivor0,Survivor1), 它们用来存放每次垃圾回收后存活下来的对象。在Old Generation中,主要存放应用程序中生命周期长的内存对象【从survivor1 剩下来的内存对象】。

大部分对象在分配时都是在Eden中

较大的对象直接分配到 Old Generation 中

如果出现java.lang.OutOfMemoryError: Java heap space异常,说明Java虚拟机的堆内存不够。 原因: 很可能是创建了大量的对象。


3.垃圾回收算法

①.Mark-Sweep(标记-清除)算法

首先标记出需要回收的对象,标记完成后统一清除对象。

缺点: 效率不高,而且会产生大量的内存碎片。

②.Copying(复制)算法

将可用内存分为两块,每次只用其中的一块,当这块内存用完以后,将还存活的对象复制到另一块上面,然后再把已经使用的内存空间一次清理掉。

优点:效率高,不会产生内存碎片。

缺点:内存缩小为原来的一半

③.Generational Collection(分代收集)算法 【JVM的垃圾收集器采用的算法】

根据不同代的特点采取最适合的收集算法。

新生代都采取Copying算法:因为大部分的对象的创建【且生命周期短】都在Young区。每次垃圾回收都要回收大部分对象。【将新生代划分为一块较大的Eden空间和两块较小的Survivor空间,每次使用Eden空间和其中的一块Survivor空间,当进行回收时,将Eden和Survivor中还存活的对象复制到另一块Survivor空间中,然后清理掉Eden和刚才使用过的Survivor空间。】

老年代的特点是每次回收都只回收少量对象,一般使用的是标记清除算法。

4.关于内存调优

原因:过多的GC和Full GC是会占用很多的CPU

目的:减少Full GC次数,减少GC频率,尽量降低CG所导致的应用线程暂停时间

手段:主要是针对内存管理方面的调优,包括控制各个代的大小,GC策略

内存控制:

①.旧生代空间不足

调优时尽量让对象在新生代GC时被回收、让对象在新生代多存活一段时间和 不要 创建过大的对象及数组 避免直接在旧生代创建对象。

②.Pemanet Generation空间不足

增大Perm Gen空间,避免太多静态对象 

③.System.gc()被显示调用

垃圾回收不要手动触发,尽量依靠JVM自身的机制

④.新生代 设置不宜过大 或过小,新生代占整个堆的1/3比较合适。


5.类加载的过程


过程就是类加载器将 所需的 (.class文件)字节码文件中要执行的代码逻辑以指令的形式 加载到 方法区,调用java方法就通过,java堆,java栈。调用native方法,就通过本地方法栈。最后执行引擎再通过PC寄存器上指令的执行顺序进行执行。


1.1 加载

加载主要是将.class文件中的二进制字节流读入到JVM中。

在加载阶段,JVM需要完成3件事:

1)通过类的全限定名获取该类的二进制字节流; 

2)将字节流所代表的静态存储结构转化为方法区的运行时数据结构; 

3)在内存中生成一个该类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口。 

1.2 连接

①. 验证

验证是连接阶段的第一步,主要确保加载进来的字节流符合JVM规范。

②.准备

为静态变量在方法区分配内存,并设置默认初始值

③.解析

虚拟机将常量池内的符号引用替换为直接引用的过程

④.初始化

类加载过程的最后一步,主要是根据程序中的赋值语句主动为类变量赋值

小知识:类加载器:类加载器实现的功能是为加载阶段获取二进制字节流的时候。


以上为 :双亲委派模型。

概念:如果一个类接受到类加载请求,他自己不会去加载这个请求,而是将这个类加载请求委派给父类加载器,这样一层一层传送,直到到达启动类加载器。 只有当父类加载器无法加载这个请求时,子加载器才会尝试自己去加载。

双亲委派模型要求除了顶层的启动类加载器外,其余的类加载器都应当有自己的父类加载器。

类加载器的作用:将.class文件加载到jvm的内存空间中。

双亲委派模式的代码实现:

1)首先检查类是否被加载,没有则调用父类加载器的loadClass()方法; 

2)若父类加载器为空,则默认使用启动类加载器作为父加载器; 

3)若父类加载失败,抛出ClassNotFoundException 异常后,再调用自己的findClass() 方法。

面试题:自己写的java.lang.String类能否被加载?

根据上边的原则 1), 我们自己写的String应该是被Bootstrap ClassLoader(顶层类加载器)加载了,所以App ClassLoader就不会再去加载我们写的String类了,导致我们写的String类是没有被加载的。


6.JMM :JAVA 内存模型 ,JVM : Java虚拟机模型。

JMM:包括 本地内存 (线程),主内存 。

本地内存 存放 着 主内存的共享变量的副本。线程在 本地内存 中运算后,将结果刷到主内存。如果线程A 与 线程B 都同时操作,这就是多线程问题的由来了。

Java 内存模型 【JMM】的抽象图:


JVM :包括方法区,堆,虚拟机栈,本地方法栈,PC寄存器。

线程共享 : 方法区,堆

线程私有:虚拟机栈(执行java方法),本地方法栈(执行native方法),PC寄存器(指令执行顺序)

7.AtomicInteger 提供原子操作的Integer类,不会有线程安全问题。十分适合高并发情况下。

保证 原子性 的秘诀 :基于CAS【比较交换】

CAS的思想很简单:主内存值 M,期望值 V,待更新值 U,Only M =V ,update V 去主内存,M≠V ,则继续对V+ 1,U+ 1,再去取主内存值M,直到V=M ,才将更新值进行更新

假设线程1和线程2通过getIntVolatile拿到value的值都为1,线程1被挂起,线程2继续执行

线程2在compareAndSwapInt操作中由于预期值和内存值都为1,因此成功将内存值更新为2

线程1继续执行,在compareAndSwapInt操作中,预期值是1,而当前的内存值为2,CAS操作失败,什么都不做,返回false

线程1重新通过getIntVolatile拿到最新的内存value为2,再进行一次compareAndSwapInt操作,这次操作成功,因为取了最新的值做为预期值,预期值是2,内存值也是2,加1更新后内存值更新为3

8.线程池:为了避免创建和销毁进程,对内存的开销,用线程池,给定数量的线程,当有请求来到,直接从线程池请求,分配线程执行请求的任务。


©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,539评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,594评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,871评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,963评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,984评论 6 393
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,763评论 1 307
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,468评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,357评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,850评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,002评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,144评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,823评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,483评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,026评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,150评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,415评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,092评论 2 355

推荐阅读更多精彩内容

  • 所有知识点已整理成app app下载地址 J2EE 部分: 1.Switch能否用string做参数? 在 Jav...
    侯蛋蛋_阅读 2,442评论 1 4
  • 第二部分 自动内存管理机制 第二章 java内存异常与内存溢出异常 运行数据区域 程序计数器:当前线程所执行的字节...
    小明oh阅读 1,172评论 0 2
  • 这篇文章解释了Java 虚拟机(JVM)的内部架构。下图显示了遵守Java SE 7 规范的典型的 JVM 核心内...
    饮墨飨书阅读 663评论 0 1
  • 工作之余,想总结一下JVM相关知识。 Java运行时数据区: Java虚拟机在执行Java程序的过程中会将其管理的...
    Huang远阅读 635评论 0 2
  • 荀子在《劝学》中,主要通过比喻论证来阐述他对“学习不可以停止”的见解,体现在文中的三个分论中。 分论点一:学习的重...
    xj明明如月阅读 26,620评论 0 2