JVM-简介

JVM的体系结构

下面是简易的结构图

栈中不会有垃圾回收

大部分JVM调优都是在堆内存上进行调优

详细的结构图如下

沙箱安全机制

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

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

​ 在java中,将执行过程分为本地代码和远程代码两部分,本地代码是默认可信任的,而远程代码则被看做是不受信任的。对于授予信任的本地代码,可以访问一切本地资源。而对于非授予信任的远程代码在早期的java实现中,完全依赖于java沙箱机制。下图是jdk1.0的安全模型

​ 但是如此严格的安全机制也给程序的功能拓展带来了障碍,比如用户希望远程代码访问本地资源的时候,就会被沙箱机制所拒绝。所以在后来的jdk1.1版本中,针对安全机制做出了改进,增加了安全策略,允许用户指定代码对本地资源的访问权限。下图是jdk1.1的安全模型

​ 在jdk1.2的版本中,再次引进安全机制,增加了代码签名,不论是本地代码还是远程代码,都会按照用户的安全策略设定,由类加载器加载到JVM中权限不同的运行空间,来实现差异化的代码执行权限控制。下图是jdk1.2的安全模型

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

​ 组成沙箱的基本组件:

  • 字节码校验器(bytecode verifier):确保java类文件遭循java语言规范。这样可以帮助ava程序实现内存保
    护。但并不是所有的类文件都会经过字节码校验,比如核心类。

  • 类装载器(classloader):其中类装载器在3个方面对java沙箱起作用

    • 它防止恶意代码去干涉善意的代码;
    • 它守护了被信任的类库边界;
    • 它将代码归入保护域,确定了代码可以进行哪些操作。

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

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

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

native

凡是带了native关键词的方法,说明这个方法java语言实现不了,此方法会被放入本地方法栈中,该栈通过JNI(Java Native Interface)去调用底层的C或者C++语言写的方法。

方法区

Method Area

方法区是被所有线程共享的,所有字段和方法字节码,以及一些特殊方法,如构造函数,接口代码也在此定义,简单来说,所有定义的方法的信息都会被保存在该区域

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

PC寄存器

程序计数器:Program Counter Register

​ 每个线程都有一个程序计数器,实现程序私有的,就是一个指针,指向方法区中的方法字节码

堆(Heap)

一个JVM只有一个堆内存,堆内存的大小是可以调节的,里面一般放实例对象

堆内存细分为三个区域:

  1. 新生区 Young/New
  2. 养老区 old
  3. 永久区 perm

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

堆内存满了就会爆OOM(OutOfMemoryError)错误

所有的对象都是在伊甸园区产生的

伊甸园满了会触发轻GC,有的就直接死了,然后留下来的去往幸存区

如果幸存区满了,就去往老年区

如果老年区满了,触发一次重GC(伊甸园区和幸存者区都清一遍,活下来的去老年区)

垃圾处理器GC

GC的作用区域只有堆

两种回收:轻GC 、Full GC

GC的算法

  1. 标记清除法
  2. 标记整理法
  3. 标记复制算法
  4. 引用计数法(给每一个对象分配一个计数器,将计数器为0的对象清除)

标记复制法

谁空谁是to幸存区

新生区主要使用复制算法

好处:没有内存碎片

坏处:浪费了内存空间

复制算法使用最佳场景:对象存活度较低的时候

标记清除法

扫描这些活着的对象,清除没有标记的对象进行清除

缺点:两次扫描,浪费时间,会产生内存碎片

优点:不需要额外空间

标记整理法

再次扫描,向一端移动存活的对象

分代收集算法

年轻代:

  • 存活率低
  • 复制算法

老年代

  • 存活率高
  • 标记清除+标记压缩混合实现

这五道面试题可以试着做一做

  1. 请你谈谈你对JVM的理解?java8虚拟机和之前的有什么不同?
  2. 什么是OOM,什么是栈溢出?怎么分析?
  3. JVM的常用调优参数有哪些?
  4. 内存快照如何抓取?怎么分析Dump文件?
  5. 谈谈你对类加载器的认识?
给作者买杯咖啡吧~~~