1、JVM的位置
运行在操作系统上,java运行在JVM上,其中Jre包含了JVM
2、java执行流程
3、类加载器
类加载器的作用:加载class文件
4、双亲委派机制:
某个特定的类加载器在接到加载类的请求时,首先将加载任务委托给父类加载器,依次递归,如果父类加载器可以完成类加载任务,就成功返回;只有父类加载器无法完成此加载任务时,才自己去加载。
3.1一道面试题
-
能不能自己写个类叫
java.lang.System
?**答案:**通常不可以,但可以采取另类方法达到这个需求。
**解释:**为了不让我们写System类,类加载采用委托机制,这样可以保证爸爸们优先,爸爸们能找到的类,儿子就没有机会加载。而System类是Bootstrap加载器加载的,就算自己重写,也总是使用Java系统提供的System,自己写的System类根本没有机会得到加载。但是,我们可以自己定义一个类加载器来达到这个目的,为了避免双亲委托机制,这个类加载器也必须是特殊的。由于系统自带的三个类加载器都加载特定目录下的类,如果我们自己的类加载器放在一个特殊的目录,那么系统的加载器就无法加载,也就是最终还是由我们自己的加载器加载。
5、沙箱安全机制:
沙箱是一个限制程序运行的环境。沙箱机制就是将 Java 代码限定在虚拟机(JVM)特定的运行范围中,并且严格限制代码对本地系统资源访问,通过这样的措施来保证对代码的有效隔离,防止对本地系统造成破坏。沙箱主要限制系统资源访问,那系统资源包括什么?——CPU、内存、文件系统、网络
。不同级别的沙箱对这些资源访问的限制也可以不一样。
所有的Java程序运行都可以指定沙箱,可以定制安全策略。
3.2.1java中的安全模型:
在Java中将执行程序分成本地代码和远程代码两种,本地代码默认视为可信任的,而远程代码则被看作是不受信的。对于授信的本地代码,可以访问一切本地资源。而对于非授信的远程代码在早期的Java实现中,安全依赖于沙箱 (Sandbox) 机制。如下图所示 JDK1.0安全模型
但如此严格的安全机制也给程序的功能扩展带来障碍,比如当用户希望远程代码访问本地系统的文件时候,就无法实现。因此在后续的 Java1.1 版本中,针对安全机制做了改进,增加了安全策略
,允许用户指定代码对本地资源的访问权限。如下图所示 JDK1.1安全模型
在 Java1.2 版本中,再次改进了安全机制,增加了代码签名
。不论本地代码或是远程代码,都会按照用户的安全策略设定,由类加载器加载到虚拟机中权限不同的运行空间,来实现差异化的代码执行权限控制。如下图所示 JDK1.2安全模型
当前最新的安全机制实现,则引入了域 (Domain) 的概念。虚拟机会把所有代码加载到不同的系统域和应用域,系统域部分专门负责与关键资源进行交互,而各个应用域部分则通过系统域的部分代理来对各种需要的资源进行访问。虚拟机中不同的受保护域 (Protected Domain),对应不一样的权限 (Permission)。存在于不同域中的类文件就具有了当前域的全部权限,如下图所示 最新的安全模型(jdk 1.6)
3.3.2组成沙箱的基本组件:
-
字节码校验器
(bytecode verifier):确保Java类文件遵循Java语言规范。这样可以帮助Java程序实现内存保护。但并不是所有的类文件都会经过字节码校验,比如核心类。 -
类装载器
(class loader):其中类装载器在3个方面对Java沙箱起作用
- 它防止恶意代码去干涉善意的代码; // 双亲委派机制
- 它守护了被信任的类库边界;
- 它将代码归入保护域,确定了代码可以进行哪些操作。
虚拟机为不同的类加载器载入的类提供不同的命名空间,命名空间由一系列唯一的名称组成,每一个被装载的类将有一个名字,这个命名空间是由Java虚拟机为每一个类装载器维护的,它们互相之间甚至不可见。
类装载器采用的机制是双亲委派模式。
- 从最内层JVM自带类加载器开始加载,外层恶意同名类得不到加载从而无法使用;
- 由于严格通过包来区分了访问域,外层恶意的类通过内置代码也无法获得权限访问到内层类,破坏代码就自然无法生效。
-
存取控制器
(access controller):存取控制器可以控制核心API对操作系统的存取权限,而这个控制的策略设定,可以由用户指定。 -
安全管理器
(security manager):是核心API和操作系统之间的主要接口。实现权限控制,比存取控制器优先级高。- 安全软件包
(security package):java.security下的类和扩展包下的类,允许用户为自己的应用增加新的安全特性,包括:
- 安全提供者
- 消息摘要
- 数字签名
- 加密
- 鉴别
6、方法区
Method Area方法区
方法区就是被所有线程共享,所有字段和方法字节码,以及一些特殊方法。如构造函数,接口代码也在此定义。,简单说。所有的定义方法保存的信息都保存在该区域,此区域属于共享区间
静态变量,常量,类信息(构造方法,接口定义)运行时的常量池也存在方法中,但是实例变量存在堆内存中,和方法区无关
7、关键字native
只要是带native关键字的,就说明java的作用范围达不到了,
- 会调用底层C语言的库!
- 进入本地方法栈!
- 调用本地方法本地接口(JNI)
- JNI作用:扩展Java的使用,融合更多的语言为Java使用。最初是C、C++
PS:java诞生的时候,C和C++横行,java想要立足,就必须要用C和C++的程序。所以就在内存中专门开辟了叫 native 的代码用来调用C和C++的代码。
8、PC寄存器
程序计数器:Program Counter Register
每个线程都有一个程序计数器,是线程私有的,就是一个指针。指向方法区中的方法字节码,(用来存储指向像一条指令的地址,也即将要执行的指令代码),在执行引擎读取下一条命令,是一个非常小的内存空间,几乎可以忽略不计~!
9、栈(Stack)
栈:数据结构
程序 = 数据结构 + 算法
栈:先进后出,后进先出
- 运行完了就会被弹出栈。
- 主要管理程序的运行,生命周期和线程同步;
- 线程结束,栈内存就释放了。
- 对于栈来说,不存在垃圾回收的问题。
- 线程结束,栈就Over!
栈:8大基本类型 + 对象引用 + 实例的方法
栈的运行原理:栈帧
栈 + 堆 + 方法区:交互关系
队列:先进先出,后进后出(FIFO:First input,First,Output)
栈:喝多了就吐。队列:出多了就拉!
10、JVM
- Sun公司
HotSpot
- BEA
JRockit
- IBM
J9 VM
11、堆(Heap)
一个JVM只有一个堆内存,堆内存的大小可以调节的。
类加载器读取了类文件后,一般会把类、方法、常量、变量,保存我们所有引用类型的真实对象。
堆内存三个区域
-
新生区(伊甸区)
- 类:诞生和成长的地方,甚至死亡
- 伊甸区:所有的对象都是在伊甸区 new 出来的
- 幸存者区
经过研究,99%都是临时对象,所以幸存者区不会轻易满(当伊甸区的对象满了就触发一次轻GC清理,清理时正在运行的对象会幸存下来,存到幸存区。)
-
养老区
-
永久区
这个区域时常驻内存的。用来存放 jdk 自身携带的 Class 对象,一般存储的是 java 的运行环境 或 类信息,不存在垃圾回收,关闭虚拟机就会释放这个区的内存 ~
永久区满的几种情况:
- 一个启动类,加载了大量的第三方Jar包
- Tomcat部署了太多的应用
- 大量动态生成的反射类
- 不断的被加载,直到内存满
名字变更
- jdk1.6之前:永久代,常量池是在方法区
- jdk1.7 :永久代,但是慢慢的退化了,常量池在堆中。
- jdk1.8之后 :无永久代,变成了元空间,常量池在元空间。
在项目中,突然出现了OOM故障,那么该如何排除?
- 第一扩大堆内存:设置堆空间:-Xms1024m -Xmx1024m -XX:+PrintGCDetails`
- 设置堆空间,如果溢出产生Dump文件
-Xms1m -Xmx8m -XX:+HeapDumpOnOutOfMemoryError
- 利用Jprofile软件查看具体的使用情况,在idea安装插件Jprofiles,