Java虚拟机对class文件采用的是按需加载的方式,也就是说当需要使用该类时才会将它的class文件加载到内存生成class对象,而且,加载某个类的class文件时,java虚拟机采用的是双亲委派机制,即请求交由父类处理,它是一种任务委派模式。
(1)如果一个类加载器受到了类加载器请求,它并不会自己先加载,而是把这个请求委托给父类的加载器去执行;
(2)如果父类加载器还存在其父类 加载器,则会进一步向上委托,依次递归,请求最终将到达顶层的引导类加载器。
如果父类加载器可以完成类加载任务,就成功返回,倘若父类加载器无法完成加载任务,自家在其才会尝试自己去加载,这就是双亲委派机制。
父类加载器一层一层往下 分配任务,如果子类加载器能加载,则加载此类,如果将加载任务分配至系统类加载器也无法加载此类,则抛出异常。
通过上面的例子,我们可以指导,双亲机制可以
Java安全模型的核心就是Java沙箱。
沙箱是一个限制程序运行的环境,沙箱机制就是将Java代码限定在虚拟机(JVM)特定的运行范围中,并且严格限制代码对本地系统资源的访问,通过这样的方式来保证对代码的有效隔离,防止对本地系统造成破坏。
那么系统资源包括什么?
CPU、内存、文件系统、网络、不同级别的沙箱对这些资源的访问限制也可以不一样。
在Java中将执行程序分为本地 代码和远程代码两种,本地代码默认为可信任的,而远程代码则认为是不受信的。对于授信的本地代码,可以访问一切本地资源;而对于非授信的远程代码在早期的Java实现中,安全依赖于沙箱机制。
JDK1.0但是如此严格的安全机制也给程序的 功能扩展带来障碍,比如当用户希望远程代码访问本地系统的文件时,就无法实现。因此在后续的Java1.1版本中,针对安全机制做了改进,增加了安全策略,允许用户指定代码对本地资源的访问权限。
在Java1.2版本中,再次改进了安全机制,增加了代码签名,不论本地代码还是远程代码,都会按照用户的安全策略设定,由类加载器加载到虚拟机中权限不同的运行空间,来实现差异化的代码执行权限控制。
当前最新的安全机制实现,则引入了域(domain)的概念,虚拟机会把所有代码加载到不同的系统域和应用域,系统域部分专门负责与关键资源进行交互,而各个应用域部分则通过系统域的部分代理来对各种需要的资源进行访问。虚拟机中不同的受保护域(Protected Domain)对应不一样的权限(Permission)。存在于不同域中的类文件就具有了当前域的全部权限。
字节码校验器(bytecode verifier):确保java类文件遵循java语言规范,这样可以帮助java程序实现内存保护,但并不是所有的类文件都会经过字节码校验,比如核心类。
类装载器(class loader):其中类装载器在3个方面对java沙箱起作用
类装载器采用的机制是双亲委派模式。
native关键字:凡是带了native关键字的,说明Java的作用范围达不到了,回去调用底层C语言的库。凡是带了native的就会进入本地方法栈,会调用本地方法接口(native method interface),即JNI。本地方法的作用就是调用本地方法库,为了扩展Java的使用,融合不同的编程语言为java所用,最初是C、C++;
Java诞生的时候,C、C++横行,要想立足,必须要有调用C、C++的程序。它在内存区域中专门开辟了一块标记区域:native method stack,用来登记native方法,在最终执行的时候,去加载本地方法库中的方法通过JNI。
例如:Java程序驱动打印机,管理系统,掌握即可,但是企业级应用较少。
调用其他接口:采取Socket,webService,Http。
程序计数器:Program Counter Register
每个线程都有一个程序计数器,是线程私有的,就是一个指针,指向方法区中的方法字节码(用来存储指向一条指令的地址,也即将要执行的指令代码),在执行引擎读取下一条指令,是一个非常小的内存空间,几乎可以忽略不计。
Method Area方法区
方法区是被所有线程共享,所有字段和方法字节码,以及一些特殊方法,如构造函数,接口代码也在此定义,简单说,所有定义的方法的信息都保存在该区域,此区域属于共享区间。
静态变量、常量、类信息(构造方法、接口定义)、运行时的常量池存在方法区中,但是实例变量存储在堆内存中,和方法无关(static,final,class,常量池)。
栈是一种数据结构
程序=数据结构+算法
程序=框架+业务逻辑
栈:先进后出(桶)
队列:先进先出 (FIFO)
为什么Main()方法先执行,后结束
栈:栈内存,主管程序的运行,生命周期和线程同步;线程结束,栈内存也就被释放。对于栈来说,不存在垃圾回收问题。一旦线程结束 ,栈就over了
栈:8大基本类型+对象引用+实例方法
栈运行原理:以栈帧
程序正在执行的方法一定在栈的顶部。
栈满了就会抛出StackOverError
Heap,一个JVM只有一个堆内存,而且堆内存的大小是可以调节的。
类加载器读取类文件后会把实例对象放到堆中(类,方法,常量,变量),保存我们所有引用类型的真实对象。
堆内存中划分为三个区域:新生代、老年代、永久代;新生区包括Eden区和幸存区;
GC垃圾回收:主要就在伊甸园区和老年代区
假设内存满了,OOM,堆内存不够!
在JDK1.8以后,永久区改为元空间;
真理:经过研究,99%的对象都是临时对象!new
这个区域常驻内存的,用来存放jdk自身携带的Class对象,interface元数据,存储的是java运行时的一些环境;这个区域不存在垃圾回收,关闭虚拟机就会释放这个区域的内存。
元空间:逻辑上存在,物理上不存在;
在一个项目中,如果出现了OOM,那么该如何排除?
研究为什么出错?
MAT、jprofiler作用:
GC分为两种类型:轻GC(普通GC)和重GC(全局GC)
GC题目:
递归遍历整个堆空间,找出无效对象进行清除,这个算法有两大缺点
将申请的内存空间分成两块,每次只用一块A,GC的时候找到活的对象复制到另外一块内存B中,清除内存A的空间。
第一步遍历出存活的对象,第二步将标记好的对象复制到内存的一端,回收以外的空间。
该算法不是 什么新算法而是根据场景,灵活运用以上三种算法而产生的算法。