一、对象的创建和初始化
1、第一次创建一个对象:
先执行父类的静态实例变量和静态代码块的初始化【这两者按照源码中的编译顺序执行,执行顺序是一样的】,然后执行子类中的静态变量和静态代码块;然后执行父类实例变量和非静态块中的初始化,最后再执行构造器(如果没有显示调用,子类默认调用的是无参构造函数,如果有super,则按super中参数来确定调用哪个构造器,如果有this,则先调用本构造器,然后再按照上面的规则决定调用哪个构造器)的初始化。然后是子类的,执行顺序同父类。
2、第二次创建对象
先执行父类实例变量和非静态块中的初始化,最后再执行构造器的初始化。然后是子类的,执行顺序同父类
二、类加载机制
2.1、类的生命周期
1、加载 :加载.class 文件到内存
2、连接:
2.1、验证:验证class文件的正确性
2.2、准备 :给类的静态变量分配内存,并分配初始值
2.3、解析:符号引用更改为直接引用(具体的地址)
3、初始化:为类的静态变量执行初始化,执行静态代码块
4、卸载:JVM回收
2.2、类加载器
Bootstrap classLoader:主要负责加载核心的类库(java.lang.*等),构造
ExtClassLoader和APPClassLoader。
ExtClassLoader:主要负责加载jre/lib/ext目录下的一些扩展的jar。
AppClassLoader:主要负责加载应用程序的主函数类
2.3、双亲委派
三、垃圾回收机制
3.1、对象的状态
1、可达状态:一个对象有一个以上引用变量引用它时,就处于可达状态
2、可恢复状态:某个对象不再被任何对象引用时,优先进入可恢复状态,在回收前,调用该对象的finalize方法
3、不可达状态:系统调用所有对象的finalize方法依然没有使该对象变成可达状态
3.2、GC Root对象
1、虚拟机栈中的引用
2、本地方法栈中的引用对象
3、静态变量或常量池引用的对象
3.3、变量引用
1、当某个变量被其他类的类变量引用时,只有该类被销毁,该对象才会进入可恢复状态
2、当某个变量被其他类的实例变量引用时,只有当引用该变量的对象被销毁,该变量才会进入可恢复状态
3.4、对象类型
强引用对象:程序中创建的对象。
软引用对象:SoftReference类,内存紧张时,垃圾回收机制会回收;内存宽松时,垃圾回收机制不会回收 可以单独使用。
弱引用对象:weakReference类 垃圾回收机制运行时,总会回收该对象 可以单独使用。
虚引用对象:主要用于跟踪对象被垃圾回收的状态 不可单独使用。
3.4、JVM回收对象的算法:是否还有对象引用指向当前对象,即从根对象出发,是否可达
3.5、垃圾回收算法
串行回收(Serial):只用一个CPU回收
并行回收(Parallel):把整个回收分成多部分,交由多个CPU回收(特点:效率高,复杂度高,易产生内存碎片);
并发执行:项目继续跑,垃圾回收也跑,特点:系统开销大,需要的堆内存高
应用程序停止:运行垃圾回收时会导致应用程序暂停
现行垃圾回收器利用分代的算法来采用不同的回收设计,按的是对象的生存时间,把堆内存分为以下三个部分
1、年轻代(young);2、年老代(old);3、永久代(permanent)
6.1、年轻代的特点:大部分对象都会很快进入不可达对象。
对年轻代的回收算法为:复制算法,即遍历可达对象
年轻代垃圾回收算法原理:
young代由一个eden区和两个Survivor区组成,绝大多数对象先分配到eden,一些比较大的对象会直接分配到OLD区,
Survivor区中的对象都至少在young经过一次垃圾回收,同一时间Survivor区一个保存对象,一个为空。每次复制都是将EDEN区
和第一个Survivor区中的可达对象复制到第二个Survivor区,然后清空EDEN区和第一个Survivor区。
eden区和Survivor的比例通过 -XX:SurvivorRatio来设置,默认为32。Survivor区太大,造成浪费,太小会造成young区中的对象提早进入OLD区
6.2、OLD区
如果young代中的对象经过多次回收,都没有被回收掉,垃圾回收机制就会将这个对象转移到OLD区。
OLD代垃圾回收的具有如下两个特征:
6.2.1、OLD代垃圾回收的执行频率无需太高,因为很少有对象会死掉
6.2.2、每次对OLD代执行垃圾回收需要更长的时间完成。
因此OLD区的垃圾回收算法采用标记压缩算法。
6.3、permanent代
permanent代主要用于装载CLASS、方法等信息,默认64M.垃圾回收机制通常不会回收permanent代中的对象。
当young代的内存将要用完时,垃圾回收机制对young代的回收,频率较高,开销小,称为次要回收;
当old代的内存将要用完时,垃圾回收机制会对young代和old代的内存进行回收,开销大,称为主要回收。
6.4、与垃圾回收的附加选项。
-Xmx:设置JAVA虚拟机堆内存的最大容量。如: -Xmx256m;
-Xms:设置JAVA虚拟机堆内存的初始容量。如: -Xms256m;
-XX:MinHeapFreeRatio=40:设置JAVA堆内存的最小空闲百分比,默认为40;
-XX:MaxHeapFreeRatio=70:设置JAVA堆内存的最大空闲百分比,默认为70;
-XX:NewRatio=2:设置young/old内存的比例;
-XX:NewSize=size:设置Young代内存的默认容量;
-XX:SurvivorRatio=8:设置Young代中的EDEN区/survivor区的比例;
-XX:MaxNewSize=size:设置Young代内存的最大容量;
-XX:PermSize=size:设置permanent代内存的默认容量;
-XX:MaxPermSize=size:设置permanent代内存的默认容量;
常用参数:http://www.oracle.com/technetwork/java/javase/tech/vmoptions-jsp-140102.html
6.5、常用垃圾回收器
1、串行回收器[YOUNG代采用串行复制算法,OLD代采用串行标记压缩算法]
通过附加选项-XX:+UseSerialGC 启动
2、并行回收器 -XX:+UseParallelGC[YOUNG代采用并行复制算法,OLD代采用串行标记压缩算法]
-XX:ParallelGCThreads=size 来指定并行线程数
3、并行压缩回收器 -XX:+UseParallelOldGC,通过 -XX:ParallelGCThreads=size 来指定并行线程数
[YOUNG代采用并行复制算法,OLD代采用并行标记压缩算法]
四、内存管理小技巧
1:尽量使用常量
2:字符串连接尽量使用StringBuilder
3:尽量少用静态变量[因为它的生命周期同这个类的生命周期,类不被卸载,它就一直存在]
4:尽量避免在循环中创建对象,如需,可以考虑使用对象数组或对象集合
5:缓存经常使用的对象
五、字符串
5.1、String,StringBuilder,StringBuffer三者的区别
1、字符串变量间的相加,是基于Stringbuilder来实现,每次追加一个字符,都需要new 一个StringBuilder对象
2、toString时,StringBuilder做了深拷贝,而StringBuffer则是共享了当前数组
3、StringBuffer 线程安全而StringBuilder非线程安全。
5.2、switch表达式 支持byte,short,char,int以及对应的包装类,枚举类和字符串类型。
六、JAVA 内存模型
Java 内存模型是一种规范,定义了很多东西:
所有的变量都存储在主内存(Main Memory)中。每个线程都有一个私有的本地内存(Local Memory),本地内存中存储了该线程以读/写共享变量的拷贝副本。线程对变量的所有操作都必须在本地内存中进行,而不能直接读写主内存。不同的线程之间无法直接访问对方本地内存中的变量。
七、反射
内省(Introspector)是Java 语言对Bean类属性、事件的一种缺省处理方法;
1、直接通过属性的描述器java.beans.PropertyDescriptor类,来访问属性的getter/setter 方法;
2、通过Introspector类
八、native方法
实现步骤:
1、 用JAVAH编译第一步生成的CLASS文件,将产生一个.H文件
2、写一个.cpp文件实现native方法,需要 包含上一步生成的.h文件
3、将上一步生成的.cpp文件编译成动态链接库文件
4、在java中用System 的loadlibrary()方法加载上一步的动态链接库文件
九、equals和hashcode
两个对象equals相等,则hashcode肯定相等,两对象hashcode相同,equals不一定相等
十、泛型
1、extends :只能获取,不能修改,它定义了容器内对象的上界,即容器内的都是 它的子类。当该类有多个子类时,如果允许添加,则会一个容器内存在多种子类类型的对象
eg: 如果允许,香蕉也会放进去
List<? extends Fruit> fruits=new ArrayList<Apple>();
// fruits.add(new Apple()); 编译报错
System.out.println(fruits.get(0));
2、super :只能修改,不能获取。定义了容器内对象的下界,即容器内的对象都是它的父类,因此不允许获取。
List<? super Fruit> apple=new ArrayList<>();
apple.add(new Apple());
3、PECS原则
1、频繁往外取,适合上界泛型
2、频繁往里插入,适合下界泛型
十一、序列化
序列化ID
1、超类是序列化的,则子类都可以正常序列化;
2、超类未实现序列化,子类实现了序列化,且超类必须有无参构造函数,则从超类继承来的实例对象会被序列化成初始值;如果父类没有无参构造函数,则会报错NotSerializableException
十二、lambda表达式
1、本质是私有的静态方法或静态内部类;与匿名内部类的区别是,它不需要在编译期间生成大量的对象而且,也不会持有外部类的成员变量
2、java stream
无状态:元素的处理不受之前元素的影响;
有状态:只有拿到所有的元素才能继续操作下去;
短路操作:不需要处理完所有元素就可以得到结果
非短路操作:必须处理完所有元素才能得到结果
性能分析:
1、数据量少于1000时,和外部迭代相比,时间差距很小
2、数据大于10000时,使用并行流(底层使用JVM的ForkJoinPool),性能会高于外部迭代
十三、线程池
参数:核心线程数,最大线程数,线程存活时间,线程存活时间单位,等待队列,生成工作线程的线程工厂,线程池的拒绝策略
十四、小知识
1、javap可以帮助开发人员深入了解编译机制,常用的:javap -c class文件的名字
2、transient :标记该字段是不被序列化和反序列化的,用来修饰成员变量