JVM

JVM 中加载Class的原理和机制

Class的加载到主要分5个阶段
    1.  加载阶段 主要由类加载器来完成
        Bootstrap ClassLoader 负责加载jre中的lib/rt.jar 同时可以指定JVM参数 -Xbootclasspath
        Extension ClassLoader 负责加载JDK/lib/ext/*.jar 扩展包下面的jar
        App ClassLoader 负责加载ClassPath下的jar
        Custom ClassLoader 自定义的类加载器
        这些加载器使用双亲委派模式加载。从下往上findClass,然后从上往下loadClass。直到抛出ClassNotFoundException  
        加载完成后将获取class的二进制流,类信息,静态变量,字节码常量放到方法区,然后再内存中生成Class 对象

    2.  验证阶段
        首先确保class文件中的字节流中包含的信息符合虚拟机规范,是安全的
        然后验证文件格式,元数据,字节码和符号引用

    3.  准备阶段 
        为类的静态变量设置默认初始值。

    4.  解析阶段
        将虚拟机常量池中的符号引用转换为直接引用。

    5.  初始化阶段
        给static字段赋予用户指定的值,静态代码块的执行

JVM 的垃圾回收

采用“分代式垃圾收集”。这种方法会跟Java对象的生命周期将堆内存划分为不同的区域,在垃圾收集过程中,可能会将对象移动到不同区域: 
 - 伊甸园(Eden):这是对象最初诞生的区域,并且对大多数对象来说,这里是它们唯一存在过的区域。 
 - 幸存者乐园(Survivor):从伊甸园幸存下来的对象会被挪到这里。 
 - 终身颐养园(Tenured):这是足够老的幸存对象的归宿。年轻代收集(Minor-GC)过程是不会触及这个地方的。
 新建的对象都是用新生代分配内存,Eden空间不足的时候,会把存活的对象转移到Survivor中,新生代大小可以由-Xmn来控制,也可以用-XX:SurvivorRatio来控制Eden和Survivor的比例旧生代。用于存放新生代中经过多次垃圾回收仍然存活的对象
 当年轻代收集不能把对象放进终身颐养园时,就会触发一次完全收集(Major-GC),这里可能还会牵扯到压缩,以便为大对象腾出足够的空间。


新生代按照8:2的比例分配了Eden区 占80%,Survivor 0 和Survivor 1各占10%,默认创建的对象都会在Eden区
当Eden没有足够的空间的时候回触发一次minor gc,然后将存活下来的对象转到s0中,然后清空eden区,再次发生minor gc 的时候将s0中的对象转到s1
当存活的对象反复在eden->s0,或者s0->s1移动的时候,GC对象的年龄也会累加,当这个年龄超过默认阈值15时,会将对象移动到老年代,
老年代用于存放几次minor gc 之后存活的对象,当老年代内存不足的时候回触发full gc
永久代在jdk8之后移除,存放到堆外内存。

JVM 内存模型

JVM各种参数的含义

-Xmx   Java Heap最大值,默认值为物理内存的1/4,最佳设值应该视物理内存大小及计算机内其他内存开销而定;
-Xms   Java Heap初始值,Server端JVM最好将-Xms和-Xmx设为相同值,开发测试机JVM可以保留默认值;
-XX:PermSize 非堆内存
-XX:MaxPermSize 非堆内存最大值

Xmx 与PermSize的和不可超过JVM可获得的总内存
PermSize不可大于Xmx

Tomcat JVM参数设置(包括打印GC 日志)

linux修改catalina.sh文件
JAVA_OPTS=”-server -Dfile.encoding=UTF-8 -Xms=512m -Xmx1024m -XX:PermSize=128m -XX:MaxPermSize=256m -verbose:gc -Xloggc:${CATALINA_HOME}/logs/gc.log`date +%Y-%m-%d-%H-%M` -XX:+UseConcMarkSweepGC -XX:+CMSIncrementalMode -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -noclassgc”

当一个对象被当作参数传递到一个方法后,此方法可改变这个对象的属性,并可返回变化后的结果,那么这里到底是值传递还是引用传递?

Java语言的方法调用只支持参数的值传递。当一个对象实例作为一个参数被传递到方法中时,参数的值就是对该对象的引用。对象的属性可以在被调用过程中被改变,但对对象引用的改变是不会影响到调用者的。

多线程

Thread类的sleep()方法和对象的wait()方法都可以让线程暂停执行,它们有什么区别?

sleep()方法(休眠)是线程类(Thread)的静态方法,调用此方法会让当前线程暂停执行指定的时间,将执行机会(CPU)让给其他线程,但是对象的锁依然保持,因此休眠时间结束后会自动恢复。
wait()是Object类的方法,调用对象的wait()方法导致当前线程放弃对象的锁(线程暂停执行),进入对象的等待池(wait pool),只有调用对象的notify()方法(或notifyAll()方法)时才能唤醒等待池中的线程进入等锁池(lock pool),如果线程重新获得对象的锁就可以进入就绪状态。

当一个线程进入一个对象的synchronized方法A之后,其它线程是否可进入此对象的synchronized方法B?

不能。其它线程只能访问该对象的非同步方法,同步方法则不能进入。因为非静态方法上的synchronized修饰符要求执行方法时要获得对象的锁,如果已经进入A方法说明对象锁已经被取走,那么试图进入B方法的线程就只能在等锁池(注意不是等待池哦)中等待对象的锁。

CAS 非阻塞同步算法
语意: “我认为V的值应该为A,如果是,那么将V的值更新为B,否则不修改并告诉V的值实际为多少”

线程池子

Java 5+中的Executor接口定义一个执行线程的工具。它的子类型即线程池接口是ExecutorService。要配置一个线程池是比较复杂的,尤其是对于线程池的原理不是很清楚的情况下,因此在工具类Executors面提供了一些静态工厂方法,生成一些常用的线程池,
如下所示: 
- newSingleThreadExecutor:创建一个单线程的线程池。这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。 
- newFixedThreadPool:创建固定大小的线程池。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。 
- newCachedThreadPool:创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程,那么就会回收部分空闲(60秒不执行任务)的线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务。此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。 
- newScheduledThreadPool:创建一个大小无限的线程池。此线程池支持定时以及周期性执行任务的需求。 

一个新的任务提交到线程池之后,线程池是如何处理的

1、线程池判断核心线程池里的线程是都在工作。如果不是,则直接执行任务。如果核心线程池里的线程都在执行任务,则执行第二步。

2、线程池判断工作队列是否已经满。如果工作队列没有满,则将新提交的任务存储在这个工作队列里进行等待。如果工作队列满了,则执行第三步

3、线程池判断线程池的线程是否都处于工作状态。如果没有,则创建一个新的工作线程来执行任务。如果已经满了,则交给饱和策略来处理这个任务

查看JVM 线程dump文件的方式
kill -3 pid 或者 jstack pid

NIO

NIO主要包括三个核心部分,Chanel,Buffer和Selector,与传统的IO相比,IO是基于字节流和字符流的操作,
而NIO基于通道和缓冲区,Selector监听通道事件,IO面向流,
NIO面向缓冲区 IO的各种流是阻塞的。这意味着,当一个线程调用read() 或 write()时,该线程被阻塞,直到有一些数据被读取,或数据完全写入。
该线程在此期间不能再干任何事情了。 NIO的非阻塞模式,使一个线程从某通道发送请求读取数据,但是它仅能得到目前可用的数据,如果目前没有数据可用时,就什么都不会获取。
而不是保持线程阻塞,所以直至数据变的可以读取之前,该线程可以继续做其他的事情。 非阻塞写也是如此。一个线程请求写入一些数据到某通道,但不需要等待它完全写入,这个线程同时可以去做别的事情。

Oracle 数据库优化

where 语句优化 避免全表扫描

  1. 查询操作避免全表扫描,在where条件和order by 条件上建立索引
  2. 避免where条件的null判断和不等于(<>,!=)判断,以及or判断,会导致全表扫描,or语句可以拆分成unin all 语句
  3. 对于连续的数字条件查询,使用between代替in
  4. where 条件避免字段做表达式操作和函数操作,应该拿到=右边操作条件
  5. where 语句条件避免@参数使用
  6. 使用exist代替in