说一下类加载机制
类加载过程:
一个class文件经过加载、连接和初始化,
加载:把一个类通过全员变量名获取这个类的二进制字节流并加载到内存中。
连接:第一步检查class文件是否符合java文件的格式、语义。第二步分配一个内存,静态变量(static)被初始化并且赋零值,static final修饰的变量这个时候也会被赋值,且赋的值为指定值。第三步把符号引用变为直接引用。
初始化:根据程序的主观设计进行初始化,加载类变量和其他资源,比如说静态代码块和一些赋值操作。
类加载机制的双亲委派机制:当一个类加载器收到一个加载类的需求的时候,他不会直接加载,是先抛给父类,会一直向上抛,直到父类可以加载。如果父加载器不能加载,则翻抛给子加载器进行加载class文件。
双亲委派机制有什么好处,如果没有双亲委派机制呢,web的类加载呢:
双亲委派机制保证了java文件的稳定性
web(tomcat)有些场景是需要双亲委派的,而有些没有双亲委派。没有双亲委派机制例如两个web服务是使用同一个jar包的不同版本,不能用双亲委派,因为不能加载不同版本了
以下为(参考答案)!!
类加载器分为
启动类加载器(核心类,负责加载jdk里的核心类,C++编写)
扩展类加载器(加载jre里的扩展库,java编写)
应用程序类加载器(加载自己写的一些代码,java编写)。
Java的双亲委派机制工作流程:一个类加载器收到加载请求后,首先并不会自己去加载,而是把请求委派给他的一个父加载器,每个加载器都是这样操作,直到最后达到顶层的启动类加载器,只有当父类自己无法完成加载请求的时候,才会让自己的子类去加载。
双亲委派机制的好处:
1、 可以防止加载重复类
2、 可以保证java的核心类不会被篡改(自己定义的java.long.string不会篡改java自身的)
Tomcat的类加载机制:默认不是双亲委派模型,但可以自己改成双亲委派模型。不是双亲委派模型就是为了将各个web应用隔离开,以防止版本问题
JVM内存模型
运行时内存划分:分为堆栈方法区,程序计数器,直接内存
程序计数器:线程执行的时候来实行代码流程,控制的什么样的顺序选择,就是选择循环这些,就是记录当前线程执行的位置,线程切回来的时候该从哪执行。线程私有的,是放在栈里面的。
栈:分为虚拟机栈和本地方法栈,因为java里有普通方法和本地方法,虚拟机栈是一个线程运行就会创建一个虚拟机栈,栈有个基本的数据结构就是栈帧,比如一个方法或一个线程会创建一个虚拟机栈,然后线程结束的时候就会销毁,然后当一个线程执行一个方法的时候就会伴随着一个栈帧的入栈,一个方法结束或发生异常的时候这些栈帧会出栈,栈帧内会存储一些局部变量表之类的东西(比如局部变量)。栈在平时会抛异常,主要是分为两种,一个是StackOverflow和oom,StackOverflow是固定了最大的栈深度,然后当递归调用没有终止条件,一直递归调用,就会一直入栈入栈,然后超过最大深度,就会StackOverflow。oom是没有设置最大的栈深度,一直调用就会把内存占满,就会oom。本地方法栈:调用native方法时去找对应的native方法。
堆:存储实例对象的地方,我们的对象基本上都存储在堆里,因为jvm他有一个自动垃圾回收机制,也就是说堆是垃圾回收机制的主要区域,因为java的对象他又不同的特性,有的对象朝生夕死,有的对象比较大,所以针对不同的对象有不同的回收算法,所以把堆分成了年轻代和老年代,年轻代分为eden区和suvivor区来对应minorGC算法,老年代对应majorGC,整个堆就是fullGC。
方法区:加载类和这类信息还有常量和静态变量,JDK1.8之后方法去移动到了虚拟机外的直接内存中
JVM性能优化
Jvm提供了一些可以排查和调优的方法,比如JDK提供的一些命令,比如jps,jstack
Jps(平时用的比较多):显示虚拟机的进程
Jstack(平时用的比较多):是线程的快照,就是这个线程对应的一些信息,线程快照就是查看线程的信息,比如说发生一些抛出异常的话也会在线程快照里显示,把整个线程的一些信息、加栈出栈都打印出来。排查问题方法(查看的关键点):比如说有时候会发生死锁,jstack信息会报死锁信息(deadlock)
Jmap:显示虚拟机的内存转储快照
Jinfo:虚拟机的配置信息
(还有个事jstat,问题和回答在下面)
查看jvm每个区占用有多大,GC次数有多少,full gc、young gc的情况,时间是多少,是如何查看
Jstat:是查看JVM内存的各种堆和非堆的大小及其内存使用量,GC的次数,可以根据垃圾回收的次数不同,去调整堆的大小
现学现卖,来测试哈呗: