一.Java
1.基础
1.1.JDK
java开发工具包= JRE+编译工具,JDK有三种版本:J2SE企业版、J2EE标准版、J2ME移动设备
JRE:java运行环境,= JVM+类库
JVM:java虚拟机,用来加载类文件,针对不同的平台,有不同的java虚拟机加载,所以java跨平台
JDK1.5:
a.并发包(java.util.concurrent) b.泛型。c.增强的for循环 d.可变参数,例:public static void main(String[] args)可以写成public static void main(String... args) e.自动包装和解包 F.枚举 g.Annotation h.静态导入(可以在使用静方法前不加类名);
JDK1.6:增加了一些易用的API即类(接口)
JDK1.7: a.增加String类型 b.Boolean类型反转,空指针安全,参与位运算
c.char之间的equals D.安全的加减乘除
JDK1.8:
A.Java 8允许我们给接口添加一个非抽象的方法实现,只需要使用 default关键字即可,这个特征又叫做扩展方法
B.对于函数体只有一行代码的,你可以去掉大括号{}以及return关键字Collections.sort(names, (a, b) -> b.compareTo(a));
C.允许使用:: 关键字来传递方法或者构造函数引用
1.2.JVM
1.JVM内存模型
一.线程私有区域
1.Program Counter Register(程序计数器):
一块较小的内存空间, 作用是当前线程所执行字节码的行号指示器(类似于传统CPU模型中的PC), PC在每次指令执行后自增, 维护下一个将要执行指令的地址. 在JVM模型中, 字节码解释器就是通过改变PC值来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖PC完成
2.虚拟机栈:虚拟机栈也就是我们平常所称的栈内存,它为java方法服务,用于存储局部变量表(基本数据类型、指向字节码指令的地址和对象引用),操作数栈、动态链接和方法出口等信息。每个方法在执行的时候都会创建一个栈帧,和数组不同的是,它不是通过索引来访问,而是压栈和出栈的方式。先放进去的后取出来。
3.本地方法栈本地方法栈和虚拟机栈类似,只不过本地方法栈为Native方法服务。
二.线程共享区域
4.堆java堆是所有线程所共享的一块内存,在虚拟机启动时创建,几乎所有的对象实例都在这里创建,因此该区域经常发生垃圾回收操作。 1.根据Java虚拟机规范,JVM将内存划分为:
New(年轻代) Tenured(年老代) 永久代(Perm)
其中New和Tenured属于堆内存,堆内存会从JVM启动参数(-Xmx:3G)指定的内存中分配,Perm不属于堆内存,有虚拟机直接分配,但可以通过-XX:PermSize -XX:MaxPermSize 等参数调整其大小。
年轻代(New):年轻代用来存放JVM刚分配的Java对象
年老代(Tenured):年轻代中经过垃圾回收没有回收掉的对象将被Copy到年老代
永久代(Perm):永久代存放Class、Method元信息,其大小跟项目的规模、类、方法的量有关,一般设置为128M就足够,设置原则是预留30%的空间。
New又分为几个部分:
Eden:Eden用来存放JVM刚分配的对象
Survivor1
Survivro2:两个Survivor空间一样大,当Eden中的对象经过垃圾回收没有被回收掉时,会在两个Survivor之间来回Copy,当满足某个条件,比如Copy次数,就会被Copy到Tenured。显然,Survivor只是增加了对象在年轻代中的逗留时间,增加了被垃圾回收的可能性。
5.Method Area(方法区)
即我们常说的永久代(Permanent Generation), 用于存储被JVM加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。方法区里有一个运行时常量池,用于存放编译期生成的各种字面量和符号引用, 字面量相当于Java语言层面常量的概念,如文本字符串,声明为final的常量值等,符号引用则属于编译原理方面的概念,包括了如下三种类型的常量:类和接口的全限定名; 字段名称和描述符; 方法名称和描述符
2.Java源码编译机制 Java代码编译是由Java源码编译器来完成Java类加载机制:类从被加载到虚拟机内存中开始,到卸载出内存为止,它的整个生命周期包括:加载、验证、准备、解析、初始化、使用和卸载7个阶段。 java的类加载机制是通过类加载器实现的。实现通过类的权限定名获取该类的二进制字节流的代码块叫做类加载器。主要有一下四种类加载器: 1. 启动类加载器(Bootstrap ClassLoader)用来加载java核心类库,无法被java程序直接引用。 2. 扩展类加载器(extensions class loader):它用来加载 Java 的扩展库。Java 虚拟机的实现会提供一个扩展库目录。该类加载器在此目录里面查找并加载 Java 类。 3. 系统类加载器(system class loader):它根据 Java 应用的类路径(CLASSPATH)来加载 Java 类。一般来说,Java 应用的类都是由它来完成加载的。可以通过 ClassLoader.getSystemClassLoader()来获取它。 4. 用户自定义类加载器,通过继承 java.lang.ClassLoader类的方式实现。Java类执行机制 :Java字节码的执行是由JVM执行引擎来完成,JVM是基于栈的体系结构来执行class字节码的。线程创建后,都会产生程序计数器(PC)和栈(Stack),程序计数器存放下一条要执行的指令在方法内的偏移量,栈中存放一个个栈帧,每个栈帧对应着每个方法的每次调用,而栈帧又是有局部变量区和操作数栈两部分组成,局部变量区用于存放方法中的局部变量和参数,操作数栈中用于存放方法执行过程中产生的中间结果。 3.jvm类加载特点双亲委派机制:类加载器会先让父类加载器加载类,只有所有的父类加载器都无法加载该类的时候,才会自己加载类。 自定义类加载器加载类时,会先把加载请求委派给父类加载器系统类加载器。系统类加载器会先把类加载请求委派给父类加载器扩展类加载器,扩展类加载器会先把类加载请求委派给父类加载器启动类加载器, 如果启动类加载器加载失败,才会使用扩展类加载来加载,如果扩展类加载器也加载失败,会使用系统类加载器来加载,如果系统类加载器也加载失败才会使用自定义类加载器来加载,如果自定义类加载器也加载失败,由于在类加载器层次结构中已经没有其他的子加载器了,所以只能报出异常ClassNotFoundException。 缓存,会把所有加载过的类都缓存起来,当需要某个类的时候会先从缓存中寻找,如果找不到才会去加载类。 除了启动类加载器,所有的类加载器都继承自java.lang.ClassLoader 3.GC什么时候触发当年轻代中Eden区内存满时,会引发一次 Minor GC(young GC),该GC仅回收年轻代。发生Minor GC之前,虚拟机会先检查老年代最大可用的连续空间是否大于新生代所有对象总空间,如果这个条件成立,那么Minor GC可以确保是安全的。如果不成立,则虚拟机会查看空间分配担保HandlePromotionFailure设置值是否允许担保失败。如果允许,那么会继续检查老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小,如果大于,将尝试着进行一次Minor GC,尽管这次Minor GC是有风险的;如果小于,或者HandlePromotionFailure设置不允许冒险,那这时也要改为进行一次Full GC。Minor GC分发之后呢会将还存活的对象放到Survivor区,放不下的会放到年老代。当年老代满时会引发Full GC,Full GC将会同时回收年轻代、年老代。当永久代满时也会引发Full GC,会导致Class、Method元信息的卸载 4.那些对象会被GC jvm对不可用的对象进行回收, 采用根搜索算法(GC Root Tracing)判断一个对象是否可用,当一个对象到GC Roots没有任何引用相连接,用图论的来说就是从GC Roots到这个对象不可达,则证明此对象是不可用的,说明此对象可以被GC。对于这些不可达对象,也不是一下子就被GC,而是至少要经历两次标记过程:如果对象在进行根搜索算法后发现没有与GC Roots相连接的引用链,那它将会第一次标记并且进行一次筛选,筛选条件是此对象有没有必要执行finalize()方法,当对象没有覆盖finalize()方法或者finalize()方法已经被虚拟机调用执行过一次,这两种情况都被视为没有必要执行finalize()方法,对于没有必要执行finalize()方法的将会被GC,对于有必要有必要执行的,对象在finalize()方法中可能会自救,也就是重新与引用链上的任何一个对象建立关联即可。 5.内存泄露、内存溢出内存泄露 memory leak,是指程序在申请内存后,无法释放已申请的内存空间,一次内存泄露危害可以忽略,但内存泄露堆积后果很严重,无论多少内存,迟早会被占光。内存溢出 out of memory,是指程序在申请内存时,没有足够的内存空间供其使用,出现out of memory;比如申请了一个integer,但给它存了long才能存下的数,那就是内存溢出。 6.怎么判断内存泄露一个程序模块系统内存和CPU资源消耗急剧增加,每次垃圾回收的时间越来越长,FullGC的时间越来越长。FullGC的次数越来越多。年老代的内存越来越大并且每次FullGC后年老代没有内存被释放。 之后系统会无法响应新的请求,逐渐到达OutOfMemoryError的临界值。JVM98%的时间都花费在内存回收,每次回收的内存小于2%,满足这两个条件将触发OutOfMemoryException 7.常见的内存泄露解决方法 a. 尽量使用直接量,对于Byte、Short、Integer、Long、Float、Double、Bolean、Character程序不应该使用new 方式创建对象,而采用直接量创建它们。 b. 使用StringBuilder和StringBuffer进行字符串连接 Sting和StringBuilder以及StringBuffer等都可以代表字符串,其中String字符串代表的是不可变的字符串,后两者表示可变的字符串。 如果使用多个String对象进行字符串连接运算,在运行时可能产生大量临时字符串,这些字符串会保存在内存中从而导致程序性能下降。 c. 尽量少使用静态变量 类的静态变量的生命周期和类同步的。在类不被卸载的情况下,类对应类对象会常驻内存,知道程序运行结束。