Java_JVM

一、Java 平台与垃圾回收
Jdk 和jre 的区别是什么
Jvm 是实现java 跨平台的核心,负责解释执行class 文件
Jre 是运行java 程序的环境的集合,包括jvm 标准实现以及java 核心类库,在编写java 程序的时候,会经常用到系统的类库,jvm 在解析执行class 文件的时候,会用到这些类库,在java 的安装目录下,通常会有bin和lib目录 ,这里的lib目录下就存放了编写代码或运行代码时所需要的类库,可以认为bin目录就是jvm,而jvm+lib =jre
Jdk 是整个java 的核心,包括java运行环境,许多开发与调试java 工具,(javac,java,javadoc,jdb,javah,javap)和java 基础的类库(包括rt.Jar) jdk = jre+java开发工具
Java类加载器的原理及其组织结构
在java 中类只有被加载到jvm中后才能运行,当运行程序时,jvm 会将编译生成的.Class 文件按照规则加载到内存中,成为一个完整的java程序,这个加载过程是由加载器来完成的,具体是由classLoader 和它的子类来实现的,类加载器本身也是一个类,把类文件从硬盘读到内存中。
类的加载方式分为隐式加载和显式加载
隐式加载:new 创建对象时候,会调用类的加载器把对应的类加载到jvm中
显式加载: 调用class.forname把所需要的类加载到jvm中

一个项目当启动的时候,类的加载是动态的,并不会一次性将所有类全部加载后再运行,保证运行的基础类(例如String类) 完全加载到jvm中,至于其他类,需要的时候才加载。
这种做法可以加快加载速度,另一方面可以节约程序运行过程中对内存的开销。,当部分类被修改的时候,重新编译变化的类即可,不需要重新编译所有的文件
Java中的类分为三种类:系统类 扩展类 自定义类分别有不同的类加载器来执行
Bootstrap loader 负责加载系统类 jre/lib/rt.jar 的类
----ExtClass Loader 负责加载扩展类 jar/lib/ext/*.jar 的类
---------AppClassLoader 负责加载应用类 classpath 或者jar 中的类
当有类被加载的时候,类加载器会请求父类来完成这个载入工作,父类会使用自己的搜索路径来搜索需要被装载的类,如果搜索不到,才会有子类按照搜索路径来搜索待加载的类。

代码…
类的加载主要分为以下三(7小部分)步:
装载:根据路径找到相对应的class文件
链接:链接又可以分为三个小的步骤
检查:检查待加载的class文件的正确性
准备:给类的静态变量分配存储空间
解析:将符号引用转换成直接引用 可忽略
初始化:对静态变量和静态代码块执行初始化工作
使用
卸载

当自定义类加载器没有指定父类加载器的情况下,默认的父类加载器即为系统类加载器。同时,我们可以得出如下结论:即使用户自定义类加载器不指定父类加载器,那么,同样可以加载如下三个地方的类:
  1. /lib下的类;
  2. < Java_Runtime_Home >/lib/ext下或者由系统变量java.ext.dir指定位置中的类;
  3. 当前工程类路径下或者由系统变量java.class.path指定位置中的类。

Jvm 的工作原理
Java_JVM_第1张图片
1、程序计数器:线程私有单元,jvm 会给每个线程创建单独的程序计数器,当前线程所执行的字节码的行号指示器,通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理线程恢复都依赖与它。
java 虚拟机的多线程是通过线程的轮流切换并分配处理器执行时间的方式来实现的。为了线程切换后能回到正确的执行位置,每条线程都需要一个程序计数器,各个线程的计数器相互不影响。
2、java虚拟机栈:线程私有的,生命周期与线程相同。每个方法执行的时候都会创建一个栈桢。用于存储局部变量表、操作栈、动态链接、方法出口等信息。每一个方法从被调用直至执行完的过程,就对应着一个栈桢在虚拟机栈中从入栈到出栈的过程。我们在学校老师讲的栈实际上就是虚拟机栈或者说是虚拟机栈中的局部变量表部分。
64位的long 和double 占用两个局部变量空间,其余的只占用一个,局部变量表所需要的内存空间在编译期间完成分配。
栈桢是方法运行期的基础数据结构。
3、本地方法栈:和虚拟机栈类似,与虚拟机栈区别是:虚拟机栈是虚拟机执行方法(字节码)服务,而本地方方法栈是虚拟机用到的native服务。
4、java堆:java堆是所有线程共享的一片区域,虚拟机启动的时候创建,此区域存放对象的实例。从内存回收的角度上来看,java堆可以细分为新生代和老年代。
Java_JVM_第2张图片
5、方法区:各个线程共享的一片内存区域,用来存放已被虚拟机所加载的类信息、常量、静态变量、即时编译器编译后的java代码,如果从内存回收的角度上来看方法区被叫做永久带,垃圾回收行为在这个区域是很少出现的。这个区域的内存回收目标主要针对常量池的回收和对类型的卸载。
运行时的常量池,最典型就是字符串常量。
6、运行时常量池:是方法区的一部分,用于存放编译期生成的各种字面量和符号引用,这部分内容将被类加载后存放到方法区内的运行时常量池中。
7、直接内存:并不是虚拟机运行时区域的一部分,也不是java的内存区域

Java 堆的参数
-Xms800m -Xmx800m -XX:MaxNewSize=256m -XX:MaxPermSize=256m -Dfile.encoding=GBK

-Xms256m JVM初始分配的堆内存, 生产环境建议与Xmx相同, 设为1024m以上
-Xmx512m JVM最大允许分配的堆内存, 生产环境建议设为1024m以上
-XX:MaxNewSize=512m JVM堆区域新生代内存的最大可分配大小(PermSize不属于堆区), 生产环境建议设为800M-1024M
-XX:MaxPermSize=128M JVM最大允许分配的非堆内存, 生产环境建议设置为256m以上
-Xmn512m 是上面两个的快捷定义方式, 等同于上面两个都为512m
Xms:初始堆大小,默认物理内存的1/64
2:Xmx:最大堆大小,默认物理内存的1/4
建议xms=xmx,好处是避免每次gc后,调整堆的大小,减少系统内存分配开销
3:Xmn:新生代大小,默认整个堆的3/8
4:-XX:NewRatio:新生与老年代的比值,不包含持久代
如果xms=xmx,且设置了xmn的情况下,该参数不需要设置
5:-XX:SurvivorRatio:Eden区和Survivor区的大小比值,设置为8,则两个Survivor区与一
个Eden区的比值为2:8,一个Survivor占整个新生的1/10
6:新建的对象也有可能直接进入老年代,比如:
大对象,可通过设置-XX:PretenureSizeThreshold=1024(单位为字节,默认为0)来
代表超过多大时就不在新生代分配,而是直接在老年代分配
7: -XX:+HeapDumpOnOutOfMemoryError:OOM时导出堆到文件
8: -XX:+HeapDumpPath:导出OOM的路径
9: -XX:OnOutOfMemoryError:在OOM时,执行一个脚本,例如:
-XX:OnOutOfMemoryError=D:/mytest.bat %p

推荐配置
1:根据实际应用来调整新生代和幸存代的大小
2:官方推荐新生代占堆的3/8
3:幸存代占新生代的1/10
4:在OOM时,记得Dump出堆,确保可以排查现场问题

垃圾回收
什么是垃圾
简单的说就是内存中已经不再被使用到的空间就是垃圾
? 引用计数法
1:给对象添加一个引用计数器,有访问就加1,引用失效就减1。
2:优点:实现简单、效率高
3:缺点:不能解决对象之间循环引用的问题
? 根搜索算法(可达性分析算法)
1:从根(GC Roots)节点向下搜索对象节点,搜索走过的路经称为引用链,当一个对象到根之间没有连通的话,则该对象不可用
Java_JVM_第3张图片

可作为GC Roots的对象包括:
虚拟机栈(栈帧局部变量)中引用的对象
方法区类静态属性引用的对象
方法区中常量引用的对象
本地方法栈中JNI引用的对象
3:HotSpot使用了一组叫做OopMap的数据结构达到准确式GC的目的:在类加载完成的时候,JVM会计算出当
前对象在哪个偏移位置上会有什么引用,这样GC扫描的时候可以很快得到引用的信息。
4:在OopMap的协助下,JVM可以很快的做完GC Roots枚举。但是JVM并没有为每一条指令生成一个OopMap,
否则会需要非常多的额外空间,反而会增加GC回收成本。因此只在特定的位置才会记录这些信息,这些
“特定的位置”被称为安全点(SafePoint),即当前线程执行到安全点后才允许暂停进行GC。
5:如果一段代码中,对象引用关系不会发生变化,这个区域中任何地方开始GC都是安全的,那么这个区域
称为安全区域(Safe Region)。安全区域可看做是扩展了的安全点。
? 引用分类
1:强引用:类似于Object a = new A()这样的,不会被回收
2:软引用:还有用但并不必须的对象。这些对象会当作回收的第二梯队,对于软引用关联着的对象,只有在内存不足的时候JVM才会回收该对象,如果回收这些对象后内存还是不
够,才发生内存溢出。用java.lang.ref.SoftReference来实现软引用。
这一点可以很好地用来解决OOM的问题,并且这个特性很适合用来实现缓存:比如网页缓存、图片缓存等。
3:弱引用:非必须对象,比软引用还要弱,垃圾回收时会回收掉。用WeakReference来实现弱引用。
WeakReference sr = new WeakReference(new String(“hello”));

    System.out.println(sr.get());
    System.gc();                //通知JVM的gc进行垃圾回收
    System.out.println(sr.get());

4:虚引用:也称为幽灵引用或幻影引用,是最弱的引用。垃圾回收时会回收掉。用PhantomReference来实
ReferenceQueue queue = new ReferenceQueue();
PhantomReference pr = new PhantomReference(new String(“hello”), queue);
System.out.println(pr.get());

现弱引用。
https://www.cnblogs.com/dolphin0520/p/3784171.html
判断类无用的条件
1:JVM中该类的所有实例都已经被回收
2:加载该类的ClassLoader已经被回收
3:没有任何地方引用该类的Class对象
4:无法在任何地方通过反射访问这个类
判断是否垃圾的步骤
1:根搜索算法判断不可用
2:看是否有必要执行finalize方法,因为这个方法还可以让对象重新被使用。当对象没有覆
盖finalize方法,或者是这个方法已经被JVM调用过,就属于没有必要执行finalize。
两个步骤走完后对象仍然没有人使用,那么就属于垃圾,可被回收。
GC类型
1:MinorGC:发生在新生代的收集动作
2:MajorGC / Full GC:全局的的GC,通常伴随至少一次的MinorGC
Stop-The-World
STW是Java中一种全局暂停的现象,多半由于GC引起。所谓全局停顿,就是所有Java
代码停止运行,native代码可以执行,但不能和JVM交互。
其危害是长时间服务停止,没有响应;对于HA系统,可能引起主备切换,严重危害
生产环境。
串行、并行、并发收集
1:串行收集:GC单线程内存回收、会暂停所有的用户线程
2:并行收集:多个GC线程并发工作,此时用户线程是暂停的
3:并发收集:用户线程和GC线程同时执行(不一定是并行,可能交替执行),不需要停顿用
户线程
4:Serial是串行的,Parallel是并行的,CMS是并发的
垃圾收集算法

垃圾收集算法——标记清除法(Mark-Sweep)
算法分成标记和清除两个阶段,先标记出要回收的对象,然后统一回收这些对象。形如:
Java_JVM_第4张图片
1:优点是简单
2:缺点是:
效率不高,标记和清除的效率都不高
标记清除后会产生大量不连续的内存碎片,从而导致在分配大对象时触发GC
垃圾收集算法——复制算法(Copying)
这种算法将可用内存按容量划分为大小相等的两块,每次只使用其中的一块,当这一块的内存用完了。就将还存活着的对象复制到另外一块上面,然后再把已经使用过的内存空间一次清理掉
Java_JVM_第5张图片
现在的商业虚拟机都采用这是采用这种收集算法来回收新生代
优点是:实现简单,运行高效,不用考虑内存碎片问题
2:缺点是:内存浪费大,只能使用一半
3:JVM实际实现中,是将内存分为一块较大的Eden区和两块较小的Survivor空间,每次使用Eden和一块
Survivor,回收时,把存活的对象复制到另一块Survivor。
4:HotSpot默认的Eden和Survivor比是8:1,也就是每次能用90%的新生代空间
5:如果Survivor空间不够,就要依赖老年代进行分配担保,把放不下的对象直接进入老年代

分配担保
分配担保是:当新生代进行垃圾回收后,新生代的存活区放置不下,那么需要把这
些对象放置到老年代去的策略,也就是老年代为新生代的GC做空间分配担保。
1:在发生MinorGC前,JVM会检查老年代的最大可用的连续空间,是否大于新生代所有对象的
总空间,如果大于,可以确保MinorGC是安全的
2:如果小于,那么JVM会检查是否设置了允许担保失败,如果允许,则继续检查老年代最大可
用的连续空间,是否大于历次晋升到老年代对象的平均大小
3:如果大于,则尝试进行一次MinorGC
4:如果不大于,则改做一次Full GC
垃圾收集算法——标记整理算法(Mark-Compact)
由于复制算法在存活对象比较多的时候,效率较低,且有空间浪费,因此老年代一
般不会选用复制算法,老年代多选用标记整理算法。
标记过程跟标记清除一样,但后续不是直接清除可回收对象,而是让所有存活对象
都向一端移动,然后直接清除边界以外的内存。形如:

Java_JVM_第6张图片

新生代选用复制算法,老年代使用标记-清除算法 或者 标记-整理算法。
垃圾收集器
前面讨论的垃圾收集算法只是内存回收的方法,垃圾收集器就来具体实现这些这些
算法并实现内存回收。不同厂商、不同版本的虚拟机实现差别很大,HotSpot中包含的收集
器如下图所示
Java_JVM_第7张图片

Serial(串行)收集器/Serial Old收集器

曾经在 jdk 1.3 是虚拟机新生代收集的唯一选择Java_JVM_第8张图片
是一个单线程的收集器,在垃圾收集时,会Stop-the-World
2:优点是简单,对于单cpu,由于没有多线程的交互开销,可能更高效,是默认的Client模式
下的新生代收集器
3:使用-XX:+UseSerialGC来开启
会使用:Serial + Serial Old 的收集器组合
4:新生代使用复制算法,老年代使用标记-整理算法
ParNew(并行)收集器
Java_JVM_第9张图片
1:使用多线程进行垃圾回收,在垃圾收集时,会Stop-the-World
2:在并发能力好的CPU环境里,它停顿的时间要比串行收集器短;但对于单cpu或并发能力较
弱的CPU,由于多线程的交互开销,可能比串行回收器更差
3:是Server模式下首选的新生代收集器,且能和CMS收集器配合使用
4:使用-XX:+UseParNewGC来开启
会使用:ParNew + Serial Old的收集器组合
5:-XX:ParallelGCThreads:指定线程数,最好与CPU数量一致
6:新生代使用复制算法,老年代采用标记-整理算法
新生代Parallel Scavenge收集器/Parallel Old收集器
是一个应用于新生代的、使用复制算法的、并行的收集器,跟ParNew很类似,但更关注吞吐量
(CPU吞吐量就是运行应用代码的时间/总运行时间,这种收集器能最高效率的利用CPU,适合运行后台
应用)。
Java_JVM_第10张图片
1:使用-XX:+UseParallelGC来开启新生代Parallel Scavenge收集器
2:使用-XX:+UseParallelOldGC来开启老年代使用Parallel Old收集器,使用Parallel Scavenge +
Parallel Old的收集器组合
3:-XX:GCTimeRatio:指定运行应用代码的时间占总时间的比例,默认99,即1%的时间用来进行垃圾收集
4:-XX:MaxGCPauseMillis:设置GC的最大停顿时间
5:新生代使用复制算法,老年代使用标记-整理算法
CMS(并发标记清除)收集器
Java_JVM_第11张图片
1:分为四个阶段
初始标记:只标记GC Roots能直接关联到的对象
并发标记:进行GC Roots Tracing的过程
重新标记:修正并发标记期间,因程序运行导致标记发生变化的那一部分对象
并发清除:并发回收垃圾对象
2:在初始标记和重新标记两个阶段还是会发生Stop-the-World
3:使用标记清除算法,也是一个使用多线程并发收集的垃圾收集器
4:最后的重置线程,指的是清空跟收集相关的数据并重置,为下一次收集做准备
4:优点:低停顿、并发执行
5:缺点:
(1)并发执行,对CPU资源压力大
(2)无法处理 在处理过程中 产生的垃圾,可能导致FGC
(3)采用的标记清除算法会导致大量碎片,从而在分配大对象是可能触发FGC
6:可设置的参数有:
-XX:UseConcMarkSweepGC:使用ParNew + CMS + Serial Old的收集器组合,Serial Old将作为CMS出错
的后备收集器
-XX:ParallelCMSThreads:设定CMS的线程数量,默认是(ParallelGCThreads+3)/4
-XX:CMSInitiatingOccupancyFraction:设置CMS收集器在老年代空间被使用多少后触发回收,默认68%
-XX:+UseCMSCompactAtFullCollection:设置CMS收集器在完成垃圾收集后是否要进行一次内存碎片的
整理,默认是开启的
-XX:CMSFullGCsBeforeCompaction:设定进行多少次CMS垃圾收集后,进行一次内存整理
-XX:+CMSClassUnloadingEnabled:允许对类元数据进行收集
-XX:UseCMSInitiatingOccupancyOnly:表示只在到达阀值的时候,才进行CMS收集
G1(Garbage-First)收集器
G1(Garbage-First)收集器
G1是一款面向服务端应用的收集器,与其它收集器相比,具有如下特点:
1:G1能充分利用多CPU、多核环境硬件优势,尽量缩短STW
2:G1仍然采用分代的思想,对存活时间较长,经过多次GC仍存活的对象,有不同的处理方
式,以获取更好的收集效果
3:G1整体上采用标记-整理算法,局部是通过复制算法,不会产生内存碎片。
4:G1把内存划分成多个独立的区域(Region),保留了新生代和老年代,但它们不再是物理隔
离的,而是一部分Region的集合,且不需要Region是连续的
5:G1的停顿可预测,能明确制定在一个时间段内,消耗在垃圾收集上的时间不能超过多长时
间。
6:G1跟踪各个Redion里面垃圾堆的价值大小,在后台维护一个优先列表,每次根据允许的时
间来回收价值最大的区域,从而保证在有限时间内的高效收集
7:跟CMS类似,也分为四个阶段
初始标记:只标记GC Roots能直接关联到的对象
并发标记:进行GC Roots Tracing的过程
最终标记:修正并发标记期间,因程序运行导致标记发生变化的那一部分对象
筛选回收:根据时间来进行价值最大化的回收
Java_JVM_第12张图片
使用和配置G1
1:-XX:+UseG1GC:开启G1
2:-XX:MaxGCPauseMillis=n:最大GC停顿时间,这是个软目标,JVM将尽可能(但不保证)停
顿小于这个时间
3:-XX:InitiatingHeapOccupancyPercent=n:堆占用了多少的时候就触发GC,默认为45
4:-XX:NewRatio=n:默认为2
5:-XX:SurvivorRatio=n:默认为8
6:-XX:MaxTenuringThreshold=n:新生代到老年代的岁数,默认是15
7:-XX:ParallelGCThreads=n:并行GC的线程数,默认值会根据平台不同而不同
8:-XX:ConcGCThreads=n:并发GC使用的线程数
9:-XX:G1ReservePercent=n:设置作为空闲空间的预留内存百分比,以降低目标空间溢出的
风险,默认值是10%
10:-XX:G1HeapRegionSize=n:设置的G1区域的大小。值是2的幂,范围是1MB到32MB。目标是
根据最小的 Java 堆大小划分出约 2048 个区域。
JVM内存配置原则
1:新生代尽可能设置大点,如果太小会导致:
YGC次数更加频繁
可能导致YGC后的对象进入老年代,如果此时老年代满了,会触发FGC
2:老年代
(1)针对响应时间优先的应用:由于老年代通常采用并发收集器,因此其大小要综合考虑并
发量和并发持续时间等参数,如果堆设置小了,可能会造成内存碎片,高回收频率会导致
应用暂停;如果堆设置大了,会需要较长的回收时间
(2)针对吞吐量优先的应用:通常设置较大的新生代和较小的老年代,这样可以尽可能回收
大部分短期对象,减少中期对象,而老年代尽量存放长期存活的对象
3:依据对象的存活周期进行分类,对象优先在新生代分配,长时间存活的对象进入老年代
4:根据不同代的特点,选取合适的收集算法
少量对象存活,适合复制算法
大量对象存活,适合标记清除或者标记整理
性能监控与故障处理工具
JVM监控工具的作用:能够辅助我们
1:运行期间对jvm内部情况进行监控,比如:对jvm参数、CPU、内存、堆等信息的查看
2:进行性能调优
3:解决应用运行时的一些,比如:
OutOfMemoryError,内存不足
内存泄露
线程死锁
锁争用(Lock Contention)
Java进程消耗CPU过高
……
常见的监测工具
1:JDK自带的,见http://docs.oracle.com/javase/8/docs/technotes/tools/
命令行工具:jps、jinfo、jstack、jmap、jhat、jstat、jstatd、jsadebugd、jcmd
可视化工具:jmc、jconsole、jvisualvm
2:yourkit
3:jProfiler
jps(JVM Process Status Tool)
jps主要用来输出JVM中运行的进程状态信息,语法格式如下:
jps [options] [hostid]
hostid字符串的语法与URI的语法基本一致:[protocol:][[//]hostname][:port][/servername] ,如
果不指定hostid,默认为当前主机或服务器。命令行参数选项说明如下:
1:-q 不输出类名、Jar名和传入main方法的参数
2:-m 输出传入main方法的参数
3:-l 输出main类或Jar的全限定名
4:-v 输出传入JVM的参数
5:-V 输出通过标识文件(.hotspotrc文件或-XX:Flags=参数指定的文件)传递给JVM的参数。
6:-Joption 传递选项参数给被javac调用的java启动程序。例如,-J-Xms48m设置启动内存为48 MB,使用-
J将选项参数传递给执行Java应用程序的底层虚拟机。
jinfo
打印给定进程或核心文件或远程调试服务器的配置信息。语法格式如下:
jinfo [ option ] pid #指定进程号(pid)的进程
jinfo [ option ] jinfo [ option ] [server-id@] #指定远程调试服务器
命令行参数选项说明如下:
1:pid:需要打印配置信息的进程ID
2:executable:产生核心dump的Java可执行文件
3:core:需要打印配置信息的核心文件
4:remote-hostname-or-IP:远程调试服务器的主机名或IP地址
5:server-id:如果相同的远程主机上运行了多台调试服务器,用此选项参数标识服务器。
6:可以用来查看正在运行的Java应用程序的扩展参数,甚至支持在运行时,修改部分参数
-flag :打印指定JVM的参数值
-flag [+|-]:设置指定JVM参数的布尔值
-flag =:设置指定JVM参数的值
jstack
jstack主要用来查看某个Java进程内的线程堆栈信息。语法格式如下:
jstack [option] pid
jstack [option] executable core
jstack [option] [server-id@]remote-hostname-or-ip
命令行参数选项说明如下:
1:-l:long listings,会打印出额外的锁信息,在发生死锁时可以用jstack -l pid来观察锁持有情况
2:-m:mixed mode,不仅会输出Java堆栈信息,还会输出C/C++堆栈信息(比如Native方法),jstack可以
定位到线程堆栈,根据堆栈信息我们可以定位到具体代码,所以它在JVM性能调优中使用得非常多。
jmap(Memory Map)
jmap用来查看堆内存使用状况,语法格式如下:
jmap [option] pid
jmap [option] executable core
jmap [option] [server-id@]remote-hostname-or-ip
如果运行在64位JVM上,可能需要指定-J-d64命令选项参数,命令行参数选项说明如下:
1:-heap:查看进程堆内存使用情况:包括使用的GC算法、堆配置参数和各代中堆内存使用
2:-histo[:live]:查看堆内存中的对象数目、大小统计直方图,如果带上live则只统计活对象
3:用jmap把进程内存使用情况dump到文件中,再用jhat、MAT、VisualVM等工具查看。命令格式如下:
jmap -dump:format=b,file=dumpFileName pid
jhat(Java Heap Analysis Tool)
用来分析jmap输出的堆信息,在堆转储文件上启动Web服务器,允许浏览堆,语法格式如下:
jhat [ options ]
注意如果Dump文件太大,可加上-J-Xmx512m参数以指定最大堆内存,即jhat -J-Xmx512m -port
8000 /home/dump.dat。然后就可以在浏览器中输入主机地址:8000查看了。命令行参数选项说明如下:
1:-stack false/true:关闭跟踪对象分配调用堆栈。注意,如果heap dump中的分配位置信息不可用,你
必须设置此标识为false。此选项的默认值为true。
2:-refs false/true:关闭对象的引用跟踪。默认为true。默认情况下,反向指针(指向给定对象的
对象,又叫做引用或外部引用)用于计算堆中的所有对象
3:-port port-number:设置jhat的HTTP服务器的端口号。默认为7000。
4:-exclude exclude-file:指定一个数据成员列表的文件,这些数据成员将被排除在“reachable
objects”查询的范围之外。比如:文件列有java.lang.String.value,那么当计算指定对象
“o”的可达对象列表时,涉及到java.lang.String.value字段的引用路径将会被忽略掉。
5:-baseline baseline-dump-file:指定一个基线heap dump。在两个heap dump(当前heap dump和
基线heap dump)中存在相同对象ID的对象,不会被标记为"new"。其他的对象将被标记为"new"。
这在比较两个不同的heap dump时非常有用
? jstat(JVM Statistics Monitoring Tool )
JVM统计监测工具,查看各个区内存和GC的情况,语法格式如下:
jstat [ generalOption | outputOptions vmid [interval[s|ms] [count]] ]
1:generalOption:单个常规的命令行选项(-help,-options,或-version)
2:outputOptions:一个或多个输出选项,由一个statOption加上任意的-t,-h和-J选项
3:vmid是虚拟机ID,在Linux/Unix系统上一般就是进程ID
4:interval是采样时间间隔
5:count是采样数目
6:可以通过jstat –options 来查看平台支持的statOptions
jstatd(JVM jstat Daemon )
虚拟机的jstat守护进程,主要用于监控JVM的创建与终止,并提供一个接口,以允
许远程监视工具附加到在本地系统上运行的JVM,语法格式:jstatd [ options ]
1:-nr:当找不到现有的RMI注册表时,不尝试使用jstatd进程创建一个内部的RMI注册表
2:-p:在指定的端口查找RMI注册表。如果没有找到,并且没有指定-nr选项,则在该端口自
行创建一个内部的RMI注册表。
3:-n:rminame,RMI注册表中绑定的RMI远程对象的名称。默认的名称为JStatRemoteHost。
如果多个jstatd服务器在同一主机上运行,你可以通过指定该选项来让每个服务器导出的
RMI对象具有唯一的名称。不管如何,这样做需要将唯一的服务器名称包含进监控客户端的
hostid和vmid字符串中。
4:-Joption:将选项参数传递给被javac调用的java启动程序
? jsadebugd
依附到一个Java进程或核心文件并且担当一个调试服务器的作用。远程客户,例如
jstack、jmap和jinfo,都能够通过Java RMI依附到该服务器。语法格式:
jsadebugd pid [ server-id ]
jsadebugd executable core [ server-id ]
jcmd(JVM Diagnostic Commands tool)
JVM诊断命令工具,将诊断命令请求发送到正在运行的Java虚拟机,比如可以用来导出
堆,查看java进程,导出线程信息,执行GC等。
1:使用jcmd或者jcmd –l能列出当前所有的虚拟机进程号,类似jps
2:jcmd 进程号 help : 列出该虚拟机支持的所有命令
3:jcmd 进程号 要执行的命令 :在该虚拟机上执行相应的命令,比如Thread.print打印线程栈信
息,又比如要导出当前堆信息,可以:jcmd 进程号 GC.heap_dump /adum.bin
? jmc(Java Mission Control )
Java任务控制(JMC)客户端包括用于监视和管理Java应用程序的工具,而不会引入通常
与这些类型的工具相关联的性能开销
? jconsole
一个用于监视Java虚拟机的符合JMX的图形工具。它可以监视本地和远程JVM,还可以监视
和管理应用程序。
? jvisualvm
一个图形工具,它提供有关在Java虚拟机中运行的基于Java技术的应用程序(Java应用程
序)的详细信息。 Java VisualVM提供内存和CPU分析,堆转储分析,内存泄漏检测,访问MBean
和垃圾回收。
两种连接方式
1: JMX连接可以查看:系统信息、CPU使用情况、线程多少、时间轴、手动执行垃圾回收等比较偏于系统级
层面的信息
2: jstatd连接方式可以提供:JVM内存分布详细信息、垃圾回收分布图、线程详细信息,甚至可以看到某
个对象使用内存的大小
因此想查看远程的JVM的具体信息,最好两种方式都进行配置连接。
? 远程连接Tomcat
1:配置JMX的支持,需要在tomcat的catalina.sh里面添加一些设置,样例如下:
CATALINA_OPTS="-Xms800m -Xmx800m -Xmn350m -XX:SurvivorRatio=8 -XX:+HeapDumpOnOutOfMemoryError
-XX:+PrintGCDetails -XX:+PrintGCDateStamps -Dcom.sun.management.jmxremote=true -
Djava.rmi.server.hostname=192.168.1.105 -Dcom.sun.management.jmxremote.port=6666 -
Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.managementote.ssl=false -
Dcom.sun.management.jmxremote.authenticate=false -XX:+UnlockCommercialFeatures -
XX:+FlightRecorder“
2:配置jstatd
(1)在JDK_HOME/jre/lib/security下面的java.policy文件里面,最后添加:permission
java.security.AllPermission;
(2)然后在JDK_HOME/bin下面运行jstatd,示例如:./jstatd -J-
Djava.rmi.server.hostname=192.168.1.105 -J-Djava.security.policy=java.policy -p 1099 &
调优实战
调什么
一:内存方面
JVM需要的内存总大小
各块内存分配,新生代、老年代、存活区
选择合适的垃圾回收算法、控制GC停顿次数和时间
解决内存泄露的问题,辅助代码优化
内存热点:检查哪些对象在系统中数量最大,辅助代码优化
二:线程方面
死锁检查,辅助代码优化
Dump线程详细信息:查看线程内部运行情况,查找竞争线程,辅助代码优化
CPU热点:检查系统哪些方法占用的大量CPU时间,辅助代码优化
如何调
1:监控JVM的状态,主要是内存、线程、代码、I/O几个部分
2:分析结果,判断是否需要优化
3:调整GC类型和内存分配;修改并优化代码
4:不断的重复分析和调整,直至找到优化的平衡点
JVM调优的目标
1:GC的时间足够的小
2:GC的次数足够的少
3:将转移到老年代的对象数量降低到最小
4:减少full GC的执行时间
5:发生Full GC的间隔足够的长
JVM调优冷思考
1:多数的Java应用不需要在服务器上进行GC优化
2:多数导致GC问题的Java应用,都不是因为我们参数设置错误,而是代码问题
3:在应用上线之前,先考虑将机器的JVM参数设置到最优(最适合)
4:GC优化是到最后不得已才采用的手段
5:在实际使用中,分析GC情况优化代码比优化GC参数要多得多
6:如下情况通常不用优化
Minor GC执行时间不到50ms
Minor GC执行不频繁,约10秒一次
Full GC执行时间不到1s
Full GC执行频率不算频繁,不低于10分钟1次
常见调优策略
1:减少创建对象的数量
2:减少使用全局变量和大对象
3:调整新生代的大小到最合适
4:设置老年代的大小为最合适
5:选择合适的GC收集器
6:将转移到老年代的对象数量降到最少
7:减少Full GC的执行时间
调优经验
1:要注意32位和64位的区别,通常32位的仅支持2-3g左右
2:要注意client模式和Server模式的选择:client runtime是快速启动,更小的内存占用以
及快速代码(机器码)生成的JIT编译器。server runtime有更复杂的代码生成优化,作为
服务型应用更为靠谱
3:要想GC时间小必须要一个更小的堆;而要保证GC次数足够少,又必须保证一个更大的堆,
这两个是有冲突的,只能取其平衡
4:针对JVM堆的设置,一般可以通过-Xms -Xmx限定其最小、最大值,为了防止垃圾收集器在最小、
最大之间收缩堆而产生额外的时间,通常把最大、最小设置为相同的值
5:新生代和老年代将根据默认的比例(1:2)分配堆内存,可以通过调整二者之间的比率NewRadio
来调整二者之间的大小,也可以通过-XX:newSize -XX:MaxNewSize来设置其绝对大小,同样,为
了防止新生的堆收缩,通常会把-XX:newSize -XX:MaxNewSize设置为同样大小
6:更大的新生代必然导致更小的老年代,大的新生代会延长普通GC的周期,但会增加每次GC的时
间;小的老年代会导致更频繁的Full GC;更小的新生代必然导致更大老年代,小的新生代会导
致普通GC很频繁,但每次的GC时间会更短;大的老年代会减少Full GC的频率
7:如果应用存在大量的临时对象,应该选择更大的新生代;如果存在相对较多的持久对象,老年代
应该适当增大。在抉择时应该本着Full GC尽量少的原则,让老年代尽量缓存常用对象,JVM的默
认比例1:2也是这个道理
8:通过观察应用一段时间,看其在峰值时老年代会占多少内存,在不影响Full GC的前提下,根据实
际情况加大新生代,比如可以把比例控制在1:1。但应该给老年代至少预留1/3的增长空间
9:线程堆栈的设置:每个线程默认会开启1M的堆栈,用于存放栈帧、调用参数、局部变量等,对大
多数应用而言这个默认值太了,一般256K就足用。在内存不变的情况下,减少每个线程的堆栈,
可以产生更多的线程
内存泄漏
一:内存泄露导致系统崩溃前的一些现象
1:每次垃圾回收的时间越来越长,FullGC的时间也延长到好几秒
2:FullGC的次数越来越多,最频繁时隔不到1分钟就进行一次FullGC
3:老年代的内存越来越大,并且每次FullGC后年老代没有内存被释放,慢慢的系统会无法响
应新的请求,逐渐到达OutOfMemoryError的临界值
二:老年代堆空间被占满的情况
1:这是非常典型的内存泄漏的垃圾回收情况,通常监控图上所有峰值部分都是一次垃圾回收
点,所有谷底部分表示是一次垃圾回收后剩余的内存。连接所有谷底的点,可以发现一条
由底到高的线,这说明随时间的推移,系统的堆空间被不断占满,最终会占满整个堆空间
2:这种情况的解决方式:一般就是根据垃圾回收前后情况对比,同时根据对象引用情况分
析,基本都可以找到泄漏点
三:堆栈溢出的情况
1:通常抛出java.lang.StackOverflowError例外
2:这个一般就是递归调用没退出,或者循环调用造成

真题解析
垃圾回收器的原理是什么?垃圾回收器是否可以马上回收内存?如何通过虚拟机进行垃圾回收
垃圾回收器主要负责完成3项任务:分配内存、确保被引用对象的内存不被错误的回收以及回收不再被引用的对象的内存空间
垃圾回收器的存在,把开发人员从释放内存的复杂的工作中解脱出来,提高了开发人员的生产效率,另一方面,对开发人员屏蔽了释放内存的方法,可以避免错误的操作内存从而导致系统的崩溃,确保稳定性
对于对象而言,如果没有任何变量去引用它,对象将不可能被程序访问,可以认为它是垃圾信息,可以被回收,只要有一个以上的变量引用该对象,该对象就不会被垃圾回收V
对于垃圾回收来说,它使用有向图来记录和管理内存中的所有对象,通过这个有向图就可以识别那些对象是 可达的 ( 有引用就是可达的),哪些对象是不可达的 (没有引用变量就是不可达的),不可达的对象是可以被垃圾回收的
public class Test5 {
public static void main(String[] args) {
Integer i1 = new Integer(1);
Integer i2 = new Integer(2);
i2 = i1;
}
Java_JVM_第13张图片

此时如果垃圾回收正在进行垃圾回收操作,在遍历上述有像图的时候,2 所占的空间是不可达的,垃圾回收认为这块内存已经不会再被使用了,释放该空间
开发人员可以通过system.gc() 方法来通知垃圾回收器运行,当然jvm 不会保证垃圾回收器马上执行,gc 方法的执行,会停止所有的响应,去检查内存中是否有可回收的对象,对性能造成极大的影响,不推荐频繁使用gc方法

for(int i=0;i<1000;i++){
Object object = new Object();
System.out.println(“obj”);
}

是否可以优化?????

如何能使jvm的堆,栈,持久带发生内存溢出
New 实例化的对象都存储在队内存中,不断的用new 实例化对象并且一直保持对象的引用,会造成队溢出
List l = new ArrayList();
While(true)
l.add(new Object());

在方法调用的时候,栈用来保存上下文的一些内容,由于栈的大小是有上限的,当出现非常深层次的方法调用的时候,就会把栈的空间用完, 最简单的栈溢出的代码就是无限递归调用,
public static void f(){
System.out.println(“111”);
f();
}

持久带 当第一个类第一次被访问的时候,jvm 需要把类加载进来,而类加载器会占用持久带的空间来存储classes 信息,持久代中主要包含以下信息 类方法、类名、常量池、和jvm使用的内部对象。当jvm需要加载一个新的类的时候,如果持久代没有足够的空间,就会抛出Java。Long.outIfMemoryError:PermGen Space 异常,代码加载足够多类的时候机会导致持久带溢出。
Java 堆被化成老年代和年轻代,他们有什么区别
根据对象的生命周期的长短吧对象分为不同的种类。年轻代、年老代、持久带,并且分别进行内存回收,也就是分代垃圾回收
分代垃圾回收算法的主要思路是 把堆分为两个货多个子堆,每一个子堆,被视为一代。在运行过程中,优先收集那些年幼的对象,如果一个对象经过多次收集仍然存货,那么久可以把这个对象转移到高一级的堆里,减少对其扫描的次数
目前最长用的jvm 就是sun 的hotSport ,他采用的就是分代回收。
HostSport 把jvm 中堆空间化为三个代 年轻代 老年代 和永久带
年轻代:分为3个部分 一个eden 去和两个相同的survivor区, eden 区主要用来存储新建对象,Survivor 也叫做from 和to区,Survivor 区是大小相等的两块区域,使用复制 回收算法时,作为双缓存,起到内存整理的作用,因此 Survivor 区始终都保持一个是空的。
老年代:主要存储生命周期比较长的对象,超大的对象(无法再新生代分配的对象)
永久代:类方法、类名、常量池、和jvm使用的内部对象、静态变量,可以持久化的数据。SunJDk 把方法区实现在了永久代。
永久代不参与垃圾回收
新建对象优先在eden区分配内存,如果eden区已满,在建设对象的时候,会因为无法申请到空间而触发minorGC操作,minorGC主要用来对年轻代垃圾进行回收:把Eden区中不能被回收的对象放入到空的survivor区,另一个survivor区不能被垃圾回收器回收的对象也会被放入到这个survivor区,这样能保证有一个survivor区是空的。如果在这个过程中发现survivor区也满了,就会把这些对象复制到老年代,或者survivor区并没有满,但是有些对象已经存在了非常长的时间,这些对象也被放到老年代中,如果老年代也被放满了,就会触发fullGC。
引申:什么情况下会触发fullGC,如何避免?
fullGC是用来清理整个堆空间,包括年轻代年老代和永久代,所以fullGC会造成很大的系统资源开销,因此,通常需要尽量避免fullGC操作。
下面介绍几种常见的fullGC产生的原因以及避免的方法。
1)调用system.gc()方法会触发fullGC,因此在编码的时候尽量避免调用这个方法。
2)老年代空间不足。由于老年代主要用来储存从年轻代转入的对象,大对象和大数组,因此,为了避免触发fullGC,应尽量做到让对象在Minor GC阶段被回收,不要创建过大的对象及数组。由于在Minor GC时,只有survivor区放不下的对象才会被放入老年代,而此时只有老年代也放不下才会触发fullGC,因此,另外一种避免fullGC的方法如下:根据实际情况增大survivor区,老年代空间或调低触发并发GC(并发垃圾回收)的概率。、
3)永久代满。永久代主要存放class相关的信息,当永久代满的时候,也会触发fullGC,为了避免这种情况的发生,可以增大永久代的空间(例如—XX:MaxPermSize=16m:设置永久代大小为16M)为了避免永久代引起的fullGC,也可以开启CMS回收永久代选项(开启的选项为+CMSPermGenSweepingEnabled-XX:+CMSClassUnloadingEnabled。CMS利用和应用程序线程并发的垃圾回收线程来进行垃圾回收操作)
需要注意的时,java8中已经移除了永久代,新增加了一个称为元数据区的native内存区,所以,大部分类的元数据都存在本地内存中分配。

二、容器
ArrayList vector LinkedList 有什么特点

ArrayList vector LinkedList 在java.util 包中,都是可以动态改变长度的数组
ArrayList 和Vector 都是基于存储元素的Object[] array 来实现的,他们会在内存中开辟一块连续的空间来存储,由于数据存储是连续的,因此他们支持下标和索引来访问元素,同时索引数据的速度比较快。但是出入元素的时候需要移动数组中的元素,所以对数据的插入操作执行速度比较慢。ArrayList 和Vector 都有 一个初始化容量的大小,当里面存储的元素超过这个大小的时候,就需要动态的扩充他们的存储空间,Vector 默认扩充为原来的两倍,(扩充的大小是可以设置的),而ArrayList 默认扩充为原来的1.5倍((oldCapacity * 3)/2 + 1)
LinkedList 采用双向列表来实现的,对数据的索引需要从列表头开始遍历,因此随机访问的效率比较低,插入元素的时候不需要对数据进行移动,插入效率比较高。LinkedList不是线程安全的。
ArrayList 和Vector 在集合的末端添加和删除元素,使用ArrayList 或者Vector 比较高。
对数据的操作主要为指定位置的出入或删除操作,使用LinkedList 效率比较高。在多线程中使用容器时 vector 比较安全
HashMap 和HashTable 有什么区别
Java 为数据结构中的映射定义了一个接口 java.util.map 。它分别有三个实现类 HashMap、hashTable和TreeMap 。Map 是用来存储键值对的数据结构,在数组中通过数组下标来对其内容进行索引,而在Map中,则是通过对象来进行索引,用来索引的对象为key,对应的对象为value
HashMap 是一个最常用的Map,根据HashCode 存储数据,根据键可以直接获取它的值,
HashMap和HashTable 都采用了Hash 方法进行索引
HashMap 允许key 和value 都为null HashTable 不允许为key 和value 都为null
HashMap 把HashTable 的contains 方法去掉了,改成了containsValue 和containsKey。
HashTable 继承dicionary ,而HashMap 是Map 接口的实现 继承AbstractMap
HashTable 是线程安全的,而HashMap不是线程安全的。
HashTable 使用 Enumeration 而HashMap 使用Iterator 进行遍历
HashTable 中hash 数组默认大小是11,增长方式是old*2+1,hashMap 数组的默认是16,而且是2的倍数
HashMap根据hashCode 得到hash值,HashTable 直接用的hashCode

HashMap和HashTable 是无序的
TreeMap 根据键进行排序
LinkedHashMap 插入顺序和读取顺序相同
Collections 和 Collection区别
Collection 是一个集合接口,实现它的类有List 和Set
Collections 是针对集合的包装类,它提供了一系列的静态方法实现对各种集合的搜索,排序和线程安全化操作

Fail-fast 和fail-safe 迭代器的区别是什么?
Fail-sage 允许在遍历的过程中对容器的数据进行修改(ConcurrentHashMap CopyOnWriteArrayList)。而fial-fast (HashMap、ArrayList)不允许。

HashSet中,equals 和hashcode 之间的关系是什么
在添加元素的时候判断两个对象的HashCode 值是否相同,不同说明两个对象不相同
如果两个对象的hashCode 相同,会判断两个对象的equals方法,相同添加,不同说明两个对象不同

ConcurrentHashMap 是java5中支持高并发高吞吐量的线程安全HashMap实现,部分上锁,扩容和默认值和HashMap相似
遍历Map的四种方式
1、for(Entry s:map.entrySet()){
S.getKey s.getValue
}
2、Iterator> s = map.entry().iterator();
While(s.hasnext()){
Map.Entry a = s.next();
A.getkey() entry.getValue();
}
3、for(String key:map.keySet()){
String value = map.get(key);
}
4、Set> s = map.entrySet();
For(Entry a:s){
a.getKey() a.getValue();
}

entrySet 效率更高一次性把key 和value 取到entry中,get 效率低 先取key,然后根据key在从map 取value

九、线程
线程,有时被称为轻量级进程(Lightweight Process,LWP),是程序执行流的最小单元
线程生命周期
1)新建状态:
使用 new 关键字和 Thread 类或其子类建立一个线程对象后,该线程对象就处于新建状态。它保持这个状态直到程序 start() 这个线程。
2)就绪状态:
当线程对象调用了start()方法之后,该线程就进入就绪状态。就绪状态的线程处于就绪队列中,要等待JVM里线程调度器的调度。
3)运行状态:
如果就绪状态的线程获取 CPU 资源,就可以执行 run(),此时线程便处于运行状态。处于运行状态的线程最为复杂,它可以变为阻塞状态、就绪状态和死亡状态。
4)阻塞状态:
如果一个线程执行了sleep(睡眠)、suspend(挂起)等方法,失去所占用资源之后,该线程就从运行状态进入阻塞状态。在睡眠时间已到或获得设备资源后可以重新进入就绪状态。可以分为三种:
4.1等待阻塞:运行状态中的线程执行 wait() 方法,使线程进入到等待阻塞状态。
4.2同步阻塞:线程在获取 synchronized 同步锁失败(因为同步锁被其他线程占用)。
4.3其他阻塞:通过调用线程的 sleep() 或 join() 发出了 I/O 请求时,线程就会进入到阻塞状态。当sleep() 状态超时,join() 等待线程终止或超时,或者 I/O 处理完毕,线程重新转入就绪状态。
5)死亡状态:
一个运行状态的线程完成任务或者其他终止条件发生时,该线程就切换到终止状态。、

实现多线程 有三种方式

三种方式有什么区别

二、线程的优先级
每一个 Java 线程都有一个优先级,这样有助于操作系统确定线程的调度顺序。
Java 线程的优先级是一个整数,其取值范围是 1 (Thread.MIN_PRIORITY ) - 10 (Thread.MAX_PRIORITY )。
默认情况下,每一个线程都会分配一个优先级 NORM_PRIORITY(5)。
具有较高优先级的线程对程序更重要,并且应该在低优先级的线程之前分配处理器资源。但是,线程优先级不能保证线程执行的顺序,而且非常依赖于平台。
三、创建线程的三种方式
通过实现 Runnable 接口;
通过继承 Thread 类本身;
通过 Callable 和 Future 创建线程
Sleep 和wait 的区别
1、原理不同 sleep 是Thread 类的静态方法,是线程用来控制自身流程的,它会使次线程暂停执行指定时间,而把执行机会让给其他线程,等到即使时间到,此线程会自动苏醒,例如(当线程执行报时功能时)每一秒打印出一个时间,那么此时就需要在打印方法前面加上一个sleep方法,以便让自己每隔一秒执行一次,该过程如同闹钟一样,而wait 是Object 类的方法,用于线程间的通信,这个方法会使当前拥有该对象锁的进程等待。直到其他线程调用notify 或者notifyAll 方法时候才会唤醒,不过开发人员也可以给它指定一个时间,自动醒来,与wait配套的方法还有notify 和notifyAll
2、对锁的机制处理不同,由于sleep方法的主要作用是让线程休眠指定的一段时间,在时间到时自动恢复,不涉及线程间的通信,调用sleep 方法不会释放锁,而wait 方法不同,当调用wait方法后,线程会释放掉它所占用的锁,从而使线程所在对象中的其他synchroinzed数据可以被其他线程使用。
3、使用区域不同。由于wait方法的特殊意义,所以必须放在同步控制方法或者同步语句块中使用,而sleep 方法则可以放在任何地方使用
4、Sleep 方法必须捕获异常,而notify notifyall 不需要捕获异常

线程同步有几种实现方法:
使用synchronized
Wait 和notify
Lock

在多线程编程时要注意哪些事项
对于对变量操作为原子的情况下用volatile 代替 synchronized,
尽量减少synchronized 块的代码
尽可能给每个线程都定义一个线程的名字,不要使用匿名线程有利于调试
尽可能使用concurrent 容器 concurrentHashMap 代替HashTable。
使用线程池来控制多线程的执行

多线程练习
一个文件中有10000个数,用java语言实现一个多线程程序,将这个10000个数输出到5个不同的文件中(不要求输出每个文件中的数量相同)。要求启动10个线程,两两一组,分为5组,每组两个线程分别将文件中的奇数和偶数输出到该对应的一个文件中,要求偶数线程每打印10个偶数,就将奇数线程打印10个奇数,如此交替进行。同时需要输出进度,每完成1000个数就在控制台中打印当前完成数量,并在所有线程结束后,在控制台输出 done

我们通过Java 提供的Condition 来实现线程的同步,Condition 在java 1.5中才出现。用来代替object 中的wait 和notify方法。实现线程的协作,相比Object类的wait()、notify 使用 Condition 的await()、signal()更加安全和高效。
Condition 依赖 Lock接口,生成一个Condition的代码为lock.NewCondition()
调用condition的await 和signal 方法必须在lock保护之内

线程池的理解
在java 中通过new Thread 的方法来创建一个新的线程执行任务,但是线程的创建时非常耗时的,而且创建出来的新线程都是各自运行、缺乏统一的管理,这样做的后果是可能导致创建过多的线程从而过度消耗系统的资源,最终导致性能极具下降,线程池的引入就是为了解决这些问题
当线程池控制线程数量时,其他线程排队等候,当一个任务执行完毕后,在从列队中取最前面的任务开始执行,如果队列中没有等待的工作线程,就可以运行了,否则进入等待队列
一方面,线程池中的线程可以被所有工作线程重复利用,一个线程可以来执行多个任务,这样做减少了线程创建的次数,另一方面,它也可以限制线程的个数。从而不会导致创建过多的线程而导致有任务运行结束,就会有空闲的线程,此线程池就会从队列里取出任务继续执行。
线程池分类
NewSingleThreadExecutor 创建一个单线程池,它只会用唯一的工作线程来执行任务,也就是相当于单线程串行执行所有任务,如果这个唯一的线程因为异常而结束,那么会有一个新的线程来代替它,
NewFixedThreadExectutor:创建一个定长线程池,可控制线程的最大并发数,超出的线程 会在队列中等待.使用这个线程池的时候,必须根据实际情况估算出线程的数量
NewCachedThreadPool 创建一个可以缓存的线程池,如果线程池的长度超过处理需要,可以灵活回收空闲线程,如果不可回收,则新建线程,此线程池不会对线程池的大小做限制,线程池的大小完全依赖于操作系统,能够创建的最大线程大小。使用这种方式需要在代码运行的过程中通过控制并发任务的数量来控制线程的数量。
NewScheduleThreadPool 创建一个定长线程池,此线程池支持定时以及周期执行任务的需求

十、Spring
Spring aop :spring aop 通过切点(aspect)指定在哪些类哪些方法织入横切逻辑,
通过增强来描述横切逻辑来植入具体的织入点(方法前方法后)
通过切面把切点和增强组织起来。

在类上添加Aspect 注解 切面

创建一个方法 在方法上加上一个@pointcut 切点 execution = “要切入的方法”

写一个方法通知 后置通知afterReturning 定义在哪个切点方法上进行业务处理 参数是 joinPoint



Redis 实现分布式锁

十一、Mysql 存储引擎
选择合适的存储引擎
MyISAM 默认的插件式的引擎,如果应用一读操作和插入操作为主,删除和修改不是很多的情况下,并且对事务的完整性,以select 和insert为主的时候用这个引擎
Innodb 用于事务处理,支持外键,对数据库要求比较高的情况下用这个。
Memery :所有数据保存在内存中,需要快速查询才能使用,对表的大小有限制,但是数据库一旦挂掉后,不可恢复
Merge:将MyLSAM 表以逻辑的方式组合在一起,作为一个对象引用他们,突破对单个myisam表的限制,并且通过将不同的表分布在多个磁盘上,有效改善merge表的访问效率。

Char 和varchar 用来存储字符串,char 固定大小,varchar 是变长的,char 用于对长度变化不大的数据,char 对行尾的空格做处理。
Myisam 用char
Innodb 用varchar

浮点数:插入数据超过定义的实际操作,会四舍五入。Float double
定点数,精确表示 decimal

触发器
Create trigger tiiger_name tigger_time tigger_event
On tbl_name for each row tiiger_stmt

Create tigger tigger_name after inster on b for each row begin
Insert into a (a,b,c) values(new.a,new.b,new.c)

存储过程
Create procedure aaa(aaa int ,bbb int ,ccc int )
Begin
Slect …
End
Java_JVM_第14张图片

十二、设计模式
开-闭原则(Open-Closed Principle, OCP)
一个软件实体应当对扩展开发,对修改关闭.说的是,再设计一个模块的时候,应当使这个模块可以在不被修改的前提下被扩展.换言之,应当可以在不必修改源代码的情况下改变这个模块的行为,在保持系统一定稳定性的基础上,对系统进行扩展。这是面向对象设计(OOD)的基石,也是最重要的原则。
里氏替换原则:通俗的来讲就是-子类可以扩展父类的功能,但不能改变父类原有的功能
里氏代换原则:子类可以在所有场合替代父类,子类可以扩展父类的功能,但不能改变父类原有的功能
依赖倒置原则:抽象不应当依赖于细节,细节应当依赖于抽象.(Program to an interface, not an implementaction)
依赖于接口,不要依赖于实现

接口隔离原则:一个类对另外一个类的依赖是建立在最小的接口上
接口中尽量少包含方法

迪米特法则:只与你直接的朋友们通信
不要跟"陌生人"说话
单一职责原则:就一个类而言,应该仅有一个引起它变化的原因,如果你能想到多于一个的动机去改变一个类,那么这个类就具有多于一个的职责.应该把多于的指责分离出去,分别再创建一些类来完成每一个职责.

你可能感兴趣的:(JAVA)