JVM快速介绍

JVM

JVM探究

  • 请你谈谈你对JVM的理解?java8虚拟机和之前的变化更新?
  • 什么是OOM,什么是栈溢出StackOverFlowError?怎么分析?
  • JVM常用调优参数有哪些?
  • 内存快照如何抓取,怎么分析Dump文件?知道吗?
  • 谈谈JVM中,类加载器你的认识?

1、JVM的位置

请添加图片描述

2、JVM的体系结构

请添加图片描述

3、类加载器

​ 一、四种类加载器

  1. 虚拟机自带的加载器
  2. 启动类(根)加载器
  3. 扩展类加载器
  4. 应用程序加载器
    请添加图片描述

4、双亲委派机制

​ 双亲委派机制:安全

​ APP–>EXC–>BOOT(最终执行)

​ 1>类加载器收到类加载的请求!

​ 2>将这个请求向上委托给父类加载器去完成,一直向上委托,直到 启动(根)类加载器

​ 3>启动加载器检查是否能够加载当前这个类,能加载就结束,使用当前额加载器,否则抛出异常,通知子加载器进行加载

​ 4>重复步骤 3

5、沙箱安全机制

​ Java 安全模型的核心就是 Java 沙箱( sandbox ),什么是沙箱?沙箱是一个限制程序运行的环境。沙箱机制就是将 Java 代码限定在虛拟机(JVM)特定的运行范围中,并且严格限制代码对本地系统资源访问,通过这样的措施有效保证对代码的有效隔离,防止对本地系统造成破坏。沙箱**主要限制系统资源访问,**那系统资源包括什么? CPU、内存、文件系统、网络。不同级別的沙箱对这些资源访问的限制也可以不一样

​ 所有的 Java 程序运行都可以指定沙箱,可以定制安全策略

​ 在 java 中将执行程序分成本地代码和远程代码两种,本地代码默认视为可信任的,而远程代码则被看作是不受信的。对于授信的本地代码,可以访问一切本地资源。而对于非授信的远程代码在早期的java实现中,安全依赖于沙箱(Sandox)机制。如下图所示 JDK1.0安全模型
请添加图片描述

​ 但如此严格的安全计件制也给程序的功能扩展带来障碍,比如当用户希望远程代码访问本地系统的文件的时候,就无法实现。因此在后续的 Java1.1版本中,针对安全机制做了改进,增加了 安全策略,允许用户指定代码对本地资源的访问权限。如下图所示 JDK1.1安全模型
请添加图片描述

​ 在java1.2版木中,再次改进了安全机制,增加了代碍签名。不论本地代码还是远程代码,都会按照用户的安全策略设定,由类加载器加载到虛拟机中权限不同的运行空间,来实现差异化的代码执行权限控制。如下图所示jDK1.2安全模型
请添加图片描述

​ 当前最新的安全机制实现,则引入了域( Domain )的概念,虚拟机会把所有代码加载到不同年系统域部分专门负责与关键资源进行交互,而各个应用域部分则通过系统域的部分代理来对各种需要的资源进行访问。虚拟机中不同的受保护域( Protected Domain ),对应不一样的权限( Permission ),存在于就具有了当前域的全部权限,如下图所示最新的安全模型( jdk 1.6)
请添加图片描述

组成沙箱的基本组件:

  • 字节码校样器( bytecode verifier ): 确保 Java 类文件遵循 java 语言规范。这样可以帮助 Java 程序实现内存保护。但并不是所有的类文件都会经过字节码校验,比如核心类。
  • 类装载器( class Ioader ): 其中类装载器在3个方面对 java 沙箱起作用。
    • 它防止恶意代码去干涉善意的代码;
    • 它守护了被信任的类库边界;
    • 它将代码归入保护域,确定了代码可以进行哪些操作。

​ 虚拟机为不同的类加载器载入的类提供不同的命名空间,命名空间由一系列唯一的名称组成,每一个被装载的类将有一个名字,这个命名空间是由 java 虚拟机为每一个类装载区维护的,它们互相之间甚至不可见

​ 类装载器采用的机制是双亲委派模式

  1. 从最内存JVM自带类加载器开始加载,外层恶意同名类得不到加载从而无法使用
  2. 由于严格通过包区分访问域,外层恶意的类通过内置代玛也无法获得权限访问到内层类,破坏代码就自然无法生效。
  • 存取控制器:存取控制器可以控制核心 API 对操作系统的存取权限,而这个控制的策略设定。可以由用户指定。
  • 安全管理器:是核心 API 和操作系统之间的主要接口,实现权限控制,比存取控制器优先级高
  • 安全软件包:java.security下的类和扩展包下的类,允许用户为自己的应用增加新的安全特性,包括。
    • 安全视供者
    • 消息摘要
    • 数字签名
    • 加密
    • 鉴别

6、Native

凡是带了 native 关键字的。说明java的作用域范围达不到了,回去调用底层C语言的库!

会进入本地方法栈

调用本地方法本地接口 JNI

JNI 的作用:扩展Java的使用,融合不同的编程语言为Java所用!最初 C,C++

Java诞生的时候C、C++、流行,想要立足 必须要有调用C、C++、的程序

它在内存区域中专门开辟了一块标记区域,Native Method Stack,登记native方法

Java程序驱动打印机,管理系统 Robot()

7、PC寄存器

又为程序计数器:Program Counter Register

​ 每个线程都有一个程序计数器,是线程私有的,就是一个指针,指向方法区的方法字节码(用来存储指向像一条指令的地址,也即将要执行的指令代码),在执行引擎读取下一条指令,是一个非常小的内存空间,几乎可以忽略不计

8、方法区

Method Area 方法区

​ 方法区是被所有线程共享,所有字段和方法节码,以及一些特殊方法,接口代码也在此定义,简单说,所有定义的方法的信息都保存在该区域,此区域属于共享区间

静态变量、常量、类信息(构造方法、接口定义)、运行时的常量池存在方法区中。但是 实例变量存在堆内存中,和方法区无关

static、final、Class、常量池

9、栈

  1. 栈:是种数据结构

    程序 = 数据结构 + 算法 :持续学习~

    程序 = 框架 + 业务逻辑 :吃饭~

    栈:先进后出、后进先出:桶

    队列:先进先出(FIFO:First Input First Output)

    喝多了吐就是栈,吃多了拉就是队列

栈:栈内存,主管程序的运行,生命周期和线程同步;

线程结束,栈内存也就是释放,对于栈来说,不存在垃圾回收问题

一旦线程结束,栈就Over!
请添加图片描述

10、三种JVM

  • Sun公司 HotSpot 市面上常见的就是它
    • Java HotSpot(TM) 64-Bit Server VM (build 25.221-b11, mixed mode)
  • BEA JRockit
  • IBMJ9 VM

11、堆

​ 又分新生去、老年区、永久区(Java8移除了它,把整个区域移到了本地内存中 命名元空间)

​ 内存调优的主要地点

​ GC垃圾回收,主要在伊甸园区和养老区

​ 假设内存满了,OOM,堆内存不够!

12、新生区、老年区

GC垃圾回收,主要是在伊甸园区和养老区~

假设内存满了,OOM,堆内存不够! java.lang.OutOfMemoryError;

在JDK8以后,永久存储区改了个名字 (元空间);
请添加图片描述

新生区

  • 类:诞生 和成长的地方,甚至死亡;
  • 伊甸园,所有的对象都是在 伊甸园 区new出来的!
  • 幸存者区 (0、1)

老年区

  • 数据成长到一定年限来到的地方,几乎很少会进行回收了

13、永久区

这个区域常驻内存的,用来存放JDK自身携带的Class对象。Interface元数据,存储的是java运行时的一些环境或类信息,这个区域不存在垃圾回收!关闭JVM虚拟机就会释放这个区域的内存~

一个启动类,加载了大量的第三方jar包。Tomcat不部署了太多的应用,大量动态生成的反射类,不断的被加载,直到内存满,就会出现OOM;

  • jdk1.6之前 :永久代,常量池在方法区;

  • jdk1.7 :永久代,但是慢慢的退化了 ‘去永久代’ ,常量池在堆中

  • jdk1.8之后 :无永久代,常量池在元空间
    请添加图片描述

逻辑上存在,物理上不存在(元空间)

14、堆内存调优

//返回虚拟机试图使用的最大内存
long max = Runtime.getRuntime().maxMemory();    //字节 1024*1024
//放回jvm的初始化总内存
long total = Runtime.getRuntime().totalMemory();
System.out.println("max="+max+"\t字节="+(max/(double)1024/1024)+"MB");
System.out.println("total="+total+"\t字节="+(total/(double)1024/1024)+"MB");
//默认情况下:分配的总内存是电脑内存的1/4,而初始化的内存:1/64
//OOM:
    //1、尝试扩大堆内存看结果
    //2、分析内存,看一下那个地方出现了问题(专业工具)
//-Xms1024m -Xmx1024m -XX:+PrintGCDetails
//305664K + 699392K = 1005056K = 918.5M
一、jvm参数分类

根据jvm参数开头可以区分参数类型,共三类:“-”、“-X”、“-XX”,

标准参数(-):所有的JVM实现都必须实现这些参数的功能,而且向后兼容;

例子:-verbose:class,-verbose:gc,-verbose:jni……

非标准参数(-X):默认jvm实现这些参数的功能,但是并不保证所有jvm实现都满足,且不保证向后兼容;

例子:Xms20m,-Xmx20m,-Xmn20m,-Xss128k……

非Stable参数(-XX):此类参数各个jvm实现会有所不同,将来可能会随时取消,需要慎重使用;

例子:-XX:+PrintGCDetails,-XX:-UseParallelGC,-XX:+PrintGCTimeStamps……

二、关键参数详解

最重要和常见的几个参数如下:

■ -Xms20m :设置jvm初始化堆大小为20m,一般与-Xmx相同避免垃圾回收完成后jvm重新分。

■-Xmx20m:设置jvm最大可用内存大小为20m。

■-Xmn10m:设置新生代大小为20m。

■-Xss128k:设置每个线程的栈大小为128k。

上面这几个参数我以前经常容易被混淆,不过后来根据字母拆分就简单了很多。

如下图:

img

还有几个GC的参数见名知意就不详解了,后面测试会一一说明,主要的如下:

■ -verbose:gc:可以输出每次GC的一些信息;

■ -XX:-UseConcMarkSweepGC:使用CMS收集器;

■ -XX:-UseParallelGC ;

■ -XX:-UseSerialGC;

■ -XX:CMSInitiatingOccupancyFraction=80 CMS gc,表示在老年代达到80%使用率时马上进行回收;

■-XX:+printGC;

■-XX:+PrintGCDetails:打印GC详情;

■-XX:+PrintGCTimeStamps:打印时间戳;

三、进行实战

在一个项目中,突然出现了OOM故障那么该如何排除研究为什么出错

  • 能够看到代码第几行出错:内存快照分析工具:MAT,Jprofiler
  • Dubug,一行行分析代码!

MAT,Jprofiler作用

  • 分析Dump内存文件,快速定位内存泄漏;
  • 获得堆中的数据
  • 获得大的对象

//-XX:+PrintGCDetails //打印GC垃圾回收信息

//-XX:+HeapDumpOnOutOfMemoryError //oom DUMP

-Xms16m -Xmx32m -XX:+HeapDumpOnOutOfMemoryError

15、GC

​ GC主要进行垃圾回收是在堆区(方法区也在堆区)

JVM在进行GC时,并不是对这三个区域统一回收。大部分回收都是新生代~

  • 新生区
  • 幸存区(form,to) 幸存0区和一区是个交替的
  • 老年区

GC两种:轻GC(普通的GC),重GC(全局GC)

题目:

  • JVM的内存模型和分区-详细到每个区放什么?
  • 堆里面的分区有哪些?Eden,from,to,老年区,说说他们的特点!
  • GC的算法有哪些?标记清除,标记压缩,复制算法,引用计数器
  • 轻GC 和 重GC 分别在什么时候发生?

16、JMM

1、什么是JMM

  • JMM:(Java Memory Model的缩写) --> java内存模型
  • Java内存模型是围绕着并发编程中原子性、可见性、有序性这三个特征来建立的
    • 原子性:一个操作不能被打断,要么全部执行完毕,要么不执行。在这点上有点类似于事务操作,要么全部执行成功,要么回退到执行该操作之前的状态。
    • 可见性:一个线程对共享变量做了修改之后,其他的线程立即能够看到(感知到)该变量的这种修改(变化)。
    • 有序性:对于一个线程的代码而言,我们总是以为代码的执行是从前往后的,依次执行的。这么说不能说完全不对,在单线程程序里,确实会这样执行;但是在多线程并发时,程序的执行就有可能出现乱序。用一句话可以总结为:在本线程内观察,操作都是有序的;如果在一个线程中观察另外一个线程,所有的操作都是无序的。前半句是指“线程内表现为串行语义(WithIn Thread As-if-Serial Semantics)”,后半句是指“指令重排”现象和“工作内存和主内存同步延迟”现象。

2、它干嘛的? 看官网,或者其他人的博客,对应的视频!

  • 作用:缓存一致性协议,用于定义数据读写的规则(遵守,找到这个规则).

  • JMM 定义了线程工作内存和主内存之间的抽象关系:线程之间的共享变量存储在主内存( Main Memory )中,每个线程都有一个私有的本地内存( Local Memory )

  • 请添加图片描述

  • 解决共享对象科技这个问题:volilate、synchronized

3、它如何学习?

  • JMM:一个抽象的概念,理论;
  • 内存交互操作
    • lock (锁定):作用于主内存的变量,把一个变量标识为线程独占状态
    • unlock (解锁):作用于主内存的变量,它把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定
    • read (读取):作用于主内存变量,它把一个变量的值从主内存传输到线程的工作内存中,以便随后的 load 动作使用
    • load (载入):作用于工作内存的变量,它把 read 操作从主存中变量放入工作内存中
    • use (使用):作用于工作内存中的变量,它把工作内存中的变量传输给执行引擎,每当虚拟机遇到一个需要使用到变量的值,就会使用到这个指令
    • assign (赋值):作用于工作内存中的变量,它把一个从执行引擎中接受到的值放入工作内存的变量副本中
    • store (存储):作用于主内存中的变量,它把一个从工作内存中一个变量的值传送到主内存中,以便后续的 write 使用
    • write (写入):作用于主内存中的变量,它把 store 操作从工作内存中得到的变量的值放入主内存的变量中
  • JMM对以上八种指令的使用,制定以下规则:
    • 不允许 read 和 load , store 和 write 操作之一单独出现。即使用了 read 必须 load ,使用了 store 必须 write
    • 不允许线程丢弃他最近的 assign 操作,即工作变量的数据改变了之后,必须告知主存。
    • 不允许一个线程将没有 assign 的数据从工作内存同步回主内存
    • 一个新的变量必须在主内存中诞生,不允许工作内存直接使用一个未被初始化的变量。就是对变量实施 use store 操作之前,必须经过 assign 和 load 操作
    • 一个变量同一时间只有一个线程能对其进行 lock 。多次 lock 后,必须执行相同次数的 unlock 才能解锁
    • 如果对一个变量进行 lock 操作,会清空所有工作内存中此变量的值,在执行引擎使用这个变量前,必须重新 load 或 assign 操作初始化变量的值
    • 如果一个变量没有被 lock ,就不能对其进行 unlock 操作。也不能 unlock 一个被其他线程锁住的变量对一个变量进行 unlock 操作之前,必须把此变量同步回主内存

JMM 对这八种操作规则和对volatile 的一些特殊规则就能确定哪里操作是线程安全,哪些操作是线程不安全的了。但是这些规则实在复杂,很难在实践中直接分析。所以一般我们也不会通过上述规则进行分析。更多的时候,使用 java 的 happen - before 规则来进行分析。

17、总结

内存效率:复制算法 > 标记清除 > 标记压缩

内存整齐度:复制算法 = 标记压缩 > 标记清除

内存利用率:标记压缩 = 标记清除 > 复制算法

思考一个问题:难道没有最优算法吗?

答案:没有,没有最好的算法,只有更适合的算法 —> GC: 分代收集算法

年轻代:

  • 存活率低 复制算法

老年代:

  • 区域大:存活率高 标记清除 + 标记压缩 一般是几次标记清除后进行一次标记压缩
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值