Day12
1. 说说类加载机制
- 类加载过程中首先判断这个类是否被加载过,如果没有被加载过,那么调用类加载器进行加载,判读这个类是否符合规范,如果不符合就会抛出异常,加载成功就会生成class对象
- 接下来就是链接过程,分为 验证,准备,解析三个阶段。验证:确保文件符合规范,不会危害虚拟机自身安全,对文件格式,字节码,元数据,符号引用进行验证。准备:为类变量分配初始空间以及默认初始值,即零值。这里不会为实例变量分配,类变量分配在方法区中,实例变量跟随对象分配在堆中,final修饰的在编译期间就分配了,在准备阶段会显式地初始化。解析:将常量池中的符号引用转为直接引用的过程。
- 链接过程完成后开始初始化的过程:初始化阶段就是执行类构造器方法的过程。此方法不需要定义,一个类只会被加载一次,虚拟机必须保证在多线程条件下类的构造方法是被加锁的
2. 说说JVM的双亲委派模型
- 双亲委派模型要求除了顶层的启动类加载器外,其余的类加载器都应有自己的父类加载器。不过这里的类加载器之前的父子关系一般不是以继承的关系来实现的,而是通常使用组合关系来复用父加载器的代码
- 双亲委派模型的工作过程是,如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的加载请求最终都应该传送到最顶层的启动类加载器中,只有当父加载器反馈自己无法完成这个加载请求时,子加载器才会尝试自己去完成加载
- 使用双亲委派模型来组织类加载器之间的关系,一个显而易见的好处就是Java中的类随着它的类加载器一起具备了一种带有优先级的层次关系。
3. 说说类的实例化过程
- 在JVM中,对象的创建遵循如下过程:
- 类加载:当JVM遇到一条字节码new指令时,首先将去检查这个指令的参数是否能在常量池中定位到一个类的符号引用,并且检查这个符号引用代表的类是否已经被加载、解析和初始化过。如果没有,那必须先执行相应的类加载过程
- 分配内存。对象所需内存的大小在类加载完成后便可完全确定,为对象分配空间的实际任务上便等同于把一块确定大小的内存块从Java堆中划分出来
- 初始化零值:内存分配完成之后,虚拟机必须将分配到的内存空间都初始化为零值,这步操作保证了对象的实例字段在Java代码中可以不赋初始值就可直接使用,使程序能够访问到这些字段的数据类型所对应的零值。
- 状态设置:接下来,虚拟机还要对对象进行必要的设置,例如这个对象是哪个类的实例,如何才能找到类的元数据信息、对象的哈希码、对象的GC分代年龄等信息,这些信息存放在对象的对象头之中
- 构造函数:Class文件中的
<init>
()方法还没有执行,所有的字段都为默认的零值,对象需要的其他资源也没有按照预定的意图构造好。一般来说,new指令之后会接着执行<init>
方法,按照程序员的意愿对对象进行初始化,这样一个真正可用的对象才算被完全构造出来
4. 请你说说内存溢出
- 内存溢出是指程序运行过程中申请的内存大于系统能够提供的内存,导致无法申请到足够的内存,于是就发生了内存溢出
- 引起内存溢出的原因:
- 内存加载的数据量过于庞大。如一次从数据库取出的过多数据
- 代码中存在死循环或者死循环中产生大量的对象实体
- 启动内存值设定过小。
- 解决内存溢出的方案:
- 修改JVM启动参数,直接增加内存
- 检查错误日志,查看“OutOfMemory”错误之前是否存在异常
- 对代码进行debug分析
- 使用内存工具动态查看内存使用情况。
- 常见的内存溢出出现在
- 堆。对象创建过多
- 栈溢出
- 方法区和运行时常量池
5. 请你说说内存泄漏
- 当长期存活的对象中引用了较短的对象,生命周期较短的对象引用可达但是又没用而导致没有进行垃圾回收而存在于内存,就造成了内存泄漏。
- 发生的情况可能有:生命周期长的对象引用短周期的对象;socket,jdbc连接不关闭;单例对象有外部对象的引用;静态集合类、释放对象时没有删除监听器
- 解决方案:及时关闭各种连接,减少static修饰变量;不用的对象设置为null;