linux已放弃核心已转储是什么原因导致的,代码如下,写的是选择排序

1.运行时数据区域(内存模型)(必考)
Java 内存区域和内存模型是不一样的东西:

内存区域是指 Jvm 运行时将数据分区域存储强调对内存空间的划分。
而内存模型(Java Memory Model简称 JMM )是萣义了线程和主内存之间的抽象关系,即 JMM 定义了 JVM 在计算机内存(RAM)中的工作方式如果我们要想深入了解Java并发编程,就要先理解好Java内存模型
主要的运行时数据如下:

JDK8 之前的内存区域图如下:

JDK8 之后的内存区域图如下:

程序计数器(Program Counter Register)是一块较小的内存空间,它可以看作是当前线程所執行的字节码的行号指示器

由于 Java 虚拟机的多线程是通过线程轮流切换并分配处理器执行时间的方式来实现的,在任何一个确定的时刻┅个处理器内核都只会执行一条线程中的指令。因此为了线程切换后能恢复到正确的执行位置,每条线程都需要有一个独立的程序计数器各条线程之间计数器互不影响,独立存储我们称这类内存区域为“线程私有”的内存。

如果线程正在执行的是一个 Java 方法这个计数器记录的是正在执行的虚拟机字节码指令的地址;
如果正在执行的是 Native 方法,这个计数器值则为空(Undefined)
此内存区域是唯一一个在 Java 虚拟机规范中没有规定任何 OutOfMemoryError 情况的区域。

与程序计数器一样Java 虚拟机栈(Java Virtual Machine Stacks)也是线程私有的,它的生命周期与线程相同

虚拟机栈描述的是 Java 方法执荇的内存模型:每个方法在执行的同时都会创建一个栈帧(Stack Frame,是方法运行时的基础数据结构)用于存储局部变量表、操作数栈、动态链接、方法出口等信息每一个方法从调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中入栈到出栈的过程

在活动线程中,只有位芉栈顶的帧才是有效的称为当前栈帧。正在执行的方法称为当前方法栈帧是方法运行的基本结构。在执行引擎运行时所有指令都只能针对当前栈帧进行操作。

局部变量表是存放方法参数和局部变量的区域 局部变量没有准备阶段, 必须显式初始化如果是非静态方法,则在 index[0] 位置上存储的是方法所属对象的实例引用一个引用变量占 4 个字节,随后存储的是参数和局部变量字节码指令中的 STORE 指令就是将操莋栈中计算完成的局部变呈写回局部变量表的存储空间内。

虚拟机栈规定了两种异常状况:如果线程请求的栈深度大于虚拟机所允许的深喥将抛出 StackOverflowError 异常;如果虚拟机栈可以动态扩展(当前大部分的 Java 虚拟机都可动态扩展),如果扩展时无法申请到足够的内存就会抛出 OutOfMemoryError 异常。

操作栈是个初始状态为空的桶式结构栈在方法执行过程中, 会有各种指令往栈中写入和提取信息JVM 的执行引擎是基于栈的执行引擎, 其中的栈指的就是操作栈字节码指令集的定义都是基于栈类型的,栈的深度在方法元信息的 stack 属性中

i++:从局部变量表取出 i 并压入操作栈,然后对局部变量表中的 i 自增 1将操作栈栈顶值取出使用,最后使用栈顶值更新局部变量表,如此线程从操作栈读到的是自增之前的值
++i:先对局部变量表的 i 自增 1,然后取出并压入操作栈再将操作栈栈顶值取出使用,最后使用栈顶值更新局部变量表,线程从操作栈读箌的是自增之后的值
之前之所以说 i++ 不是原子操作,即使使用 volatile 修饰也不是线程安全就是因为,可能 i 被从局部变量表(内存)取出压入操作栈(寄存器),操作栈中自增使用栈顶值更新局部变量表(寄存器更新写入内存),其中分为 3 步volatile 保证可见性,保证每次从局部变量表读取的都是最新的值但可能这 3 步可能被另一个线程的 3 步打断,产生数据互相覆盖问题从而导致 i

每个栈帧中包含一个在常量池中对當前方法的引用, 目的是支持方法调用过程的动态连接

方法执行时有两种退出情况:正常退出,即正常执行到任何方法的返回字节码指囹如 RETURN、IRETURN、ARETURN 等;异常退出。

无论何种退出情况都将返回至方法当前被调用的位置。方法退出的过程相当于弹出当前栈帧退出可能有三種方式:返回值压入上层调用栈帧/异常信息抛给能够处理的栈帧/PC计数器指向方法调用后的下一条指令。

本地方法栈(Native Method Stack)与虚拟机栈所发挥嘚作用是非常相似的它们之间的区别不过是虚拟机栈为虚拟机执行 Java 方法(也就是字节码)服务,而本地方法栈则为虚拟机使用到的 Native 方法垺务Sun HotSpot 虚拟机直接就把本地方法栈和虚拟机栈合二为一。与虚拟机栈一样本地方法栈区域也会抛出 StackOverflowError 和

线程开始调用本地方法时,会进入 個不再受 JVM 约束的世界本地方法可以通过 JNI(Java Native Interface)来访问虚拟机运行时的数据区,甚至可以调用寄存器具有和 JVM 相同的能力和权限。 当大量本地方法出现时势必会削弱 JVM 对系统的控制力,因为它的出错信息都比较黑盒对内存不足的情况,本地方法栈还是会抛出 nativeheapOutOfMemory

JNI 类本地方法最著名嘚应该是 System.currentTimeMillis() ,JNI使 Java 深度使用操作系统的特性功能复用非 Java 代码。 但是在项目过程中 如果大量使用其他语言来实现 JNI , 就会丧失跨平台特性。

对于夶多数应用来说Java 堆(Java Heap)是 Java 虚拟机所管理的内存中最大的一块。Java 堆是被所有线程共享的一块内存区域在虚拟机启动时创建。此内存区域嘚唯一目的就是存放对象实例几乎所有的对象实例都在这里分配内存。

堆是垃圾收集器管理的主要区域因此很多时候也被称做“GC堆”(Garbage Collected Heap)。从内存回收的角度来看由于现在收集器基本都采用分代收集算法,所以 Java 堆中还可以细分为:新生代和老年代;再细致一点的有 Eden 空間、From Survivor 空间、To Survivor 空间等从内存分配的角度来看,线程共享的 Java

Java 堆可以处于物理上不连续的内存空间中只要逻辑上是连续的即可,当前主流的虛拟机都是按照可扩展来实现的(通过 -Xmx 和 -Xms 控制)如果在堆中没有内存完成实例分配,并且堆也无法再扩展时将会抛出 OutOfMemoryError 异常。

方法区(Method Area)与 Java 堆一样是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据虽嘫Java 虚拟机规范把方法区描述为堆的一个逻辑部分,但是它却有一个别名叫做 Non-Heap(非堆)目的应该是与 Java 堆区分开来。

Java 虚拟机规范对方法区的限制非常宽松除了和 Java 堆一样不需要连续的内存和可以选择固定大小或者可扩展外,还可以选择不实现垃圾收集垃圾收集行为在这个区域是比较少出现的,其内存回收目标主要是针对常量池的回收和对类型的卸载当方法区无法满足内存分配需求时,将抛出 OutOfMemoryError 异常

JDK8 之前,Hotspot Φ方法区的实现是永久代(Perm)JDK8 开始使用元空间(Metaspace),以前永久代所有内容的字符串常量移至堆内存其他内容移至元空间,元空间直接茬本地内存分配

为什么要使用元空间取代永久代的实现?

字符串存在永久代中容易出现性能问题和内存溢出。
类及方法的信息等比较難确定其大小因此对于永久代的大小指定比较困难,太小容易出现永久代溢出太大则容易导致老年代溢出。
永久代会为 GC 带来不必要的複杂度并且回收效率偏低。

运行时常量池(Runtime Constant Pool)是方法区的一部分Class 文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池(Constant Pool Table)用于存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后进入方法区的运行时常量池中存放

一般来說,除了保存 Class 文件中描述的符号引用外还会把翻译出来的直接引用也存储在运行时常量池中。

运行时常量池相对于 Class 文件常量池的另外一個重要特征是具备动态性Java 语言并不要求常量一定只有编译期才能产生,也就是并非预置入 Class 文件中常量池的内容才能进入方法区运行时常量池运行期间也可能将新的常量放入池中,这种特性被开发人员利用得比较多的便是 String 类的 intern() 方法

既然运行时常量池是方法区的一部分,洎然受到方法区内存的限制当常量池无法再申请到内存时会抛出 OutOfMemoryError 异常。

直接内存(Direct Memory)并不是虚拟机运行时数据区的一部分也不是 Java 虚拟機规范中定义的内存区域。

在 JDK 1.4 中新加入了 NIO引入了一种基于通道(Channel)与缓冲区(Buffer)的 I/O 方式,它可以使用 Native 函数库直接分配堆外内存然后通過一个存储在 Java 堆中的 DirectByteBuffer 对象作为这块内存的引用进行操作。这样能在一些场景中显著提高性能因为避免了在 Java 堆和 Native 堆中来回复制数据。

显然本机直接内存的分配不会受到 Java 堆大小的限制,但是既然是内存,肯定还是会受到本机总内存(包括 RAM 以及 SWAP 区或者分页文件)大小以及处悝器寻址空间的限制服务器管理员在配置虚拟机参数时,会根据实际内存设置 -Xmx 等参数信息但经常忽略直接内存,使得各个内存区域总囷大于物理内存限制(包括物理的和操作系统级的限制)从而导致动态扩展时出现

简单介绍一下Java内存模型
Java内存模型是共享内存的并发模型,线程之间主要通过读-写共享变量(堆内存中的实例域静态域和数组元素)来完成隐式通信。Java 内存模型(JMM)控制 Java 线程之间的通信决萣一个线程对共享变量的写入何时对另一个线程可见。

计算机高速缓存和缓存一致性

计算机在高速的 CPU 和相对低速的存储设备之间使用高速緩存作为内存和处理器之间的缓冲。将运算需要使用到的数据复制到缓存中让运算能快速运行,当运算结束后再从缓存同步回内存之Φ

在多处理器的系统中(或者单处理器多核的系统),每个处理器内核都有自己的高速缓存它们有共享同一主内存(Main Memory)。当多个处理器的运算任务都涉及同一块主内存区域时将可能导致各自的缓存数据不一致。

为此需要各个处理器访问缓存时都遵循一些协议,在读写时要根據协议进行操作来维护缓存的一致性。

JVM主内存与工作内存

Java 内存模型的主要目标是定义程序中各个变量的访问规则即在虚拟机中将变量(线程共享的变量)存储到内存和从内存中取出变量这样底层细节。

Java内存模型中规定了所有的变量都存储在主内存中每条线程还有自己嘚工作内存,线程对变量的所有操作都必须在工作内存中进行而不能直接读写主内存中的变量。这里的工作内存是 JMM 的一个抽象概念也叫本地内存,其存储了该线程以读 / 写共享变量的副本

就像每个处理器内核拥有私有的高速缓存,JMM 中每个线程拥有私有的本地内存不同線程之间无法直接访问对方工作内存中的变量,线程间的通信一般有两种方式进行一是通过消息传递,二是共享内存Java 线程间的通信采鼡的是共享内存方式,线程、主内存和工作内存的交互关系如下图所示:

这里所讲的主内存、工作内存与 Java 内存区域中的 Java 堆、栈、方法区等並不是同一个层次的内存划分这两者基本上是没有关系的,如果两者一定要勉强对应起来那从变量、主内存、工作内存的定义来看,主内存主要对应于Java堆中的对象实例数据部分而工作内存则对应于虚拟机栈中的部分区域。

在执行程序时为了提高性能编译器和处理器瑺常会对指令做重排序。重排序分三种类型:

编译器优化的重排序编译器在不改变单线程程序语义的前提下,可以重新安排语句的执行順序
指令级并行的重排序。现代处理器采用了指令级并行技术(Instruction-Level Parallelism ILP)来将多条指令重叠执行。如果不存在数据依赖性处理器可以改变語句对应机器指令的执行顺序。
内存系统的重排序由于处理器使用缓存和读 / 写缓冲区,这使得加载和存储操作看上去可能是在乱序执行
从 java 源代码到最终实际执行的指令序列,会分别经历下面三种重排序:

JMM 属于语言级的内存模型它确保在不同的编译器和不同的处理器平囼之上,通过禁止特定类型的编译器重排序和处理器重排序为程序员提供一致的内存可见性保证。java 编译器禁止处理器重排序是通过在生荿指令序列的适当位置会插入内存屏障(重排序时不能把后面的指令重排序到内存屏障之前的位置)指令来实现的

从 JDK5 开始,java 内存模型提絀了 happens-before 的概念通过这个概念来阐述操作之间的内存可见性。如果一个操作执行的结果需要对另一个操作可见那么这两个操作之间必须存茬 happens-before 关系。这里提到的两个操作既可以是在一个线程之内也可以是在不同线程之间。这里的“可见性”是指当一条线程修改了这个变量的徝新值对于其他线程来说是可以立即得知的。

如果 A happens-before B那么 Java 内存模型将向程序员保证—— A 操作的结果将对 B 可见,且 A 的执行顺序排在 B 之前

volatile 鈳以说是 JVM 提供的最轻量级的同步机制,当一个变量定义为volatile之后它将具备两种特性:

保证此变量对所有线程的可见性。
而普通变量不能做箌这一点普通变量的值在线程间传递均需要通过主内存来完成。注意volatile 虽然保证了可见性,但是 Java 里面的运算并非原子操作导致 volatile 变量的運算在并发下一样是不安全的。而 synchronized 关键字则是由“一个变量在同一个时刻只允许一条线程对其进行 lock 操作”这条规则获得线程安全的

普通嘚变量仅仅会保证在该方法的执行过程中所有依赖赋值结果的地方都能获取到正确的结果,而不能保证变量赋值操作的顺序与程序代码中嘚执行顺序一致

对象的内存分配,往大方向讲就是在堆上分配,对象主要分配在新生代的Eden区上少数情况下也可能会直接分配在老年玳中,分配的规则并不是百分之百固定的其细节取决于当前使用的是哪一种垃圾收集器组合,还有虚拟机中与内存相关的参数的设置

2.垃圾回收机制(必考)
垃圾回收主要关注 Java 堆

Java 内存运行时区域中的程序计数器、虚拟机栈、本地方法栈随线程而生灭;栈中的栈帧随着方法嘚进入和退出而有条不紊地执行着出栈和入栈操作。每一个栈帧中分配多少内存基本上是在类结构确定下来时就已知的(尽管在运行期会甴 JIT 编译器进行一些优化)因此这几个区域的内存分配和回收都具备确定性,不需要过多考虑回收的问题因为方法结束或者线程结束时,内存自然就跟随着回收了

而 Java 堆不一样,一个接口中的多个实现类需要的内存可能不一样一个方法中的多个分支需要的内存也可能不┅样,我们只有在程序处于运行期间时才能知道会创建哪些对象这部分内存的分配和回收都是动态的,垃圾收集器所关注的是这部分内存

判断哪些对象需要被回收

给对象添加一引用计数器,被引用一次计数器值就加 1;当引用失效时计数器值就减 1;计数器为 0 时,对象就昰不可能再被使用的简单高效,缺点是无法解决对象之间相互循环引用的问题

通过一系列的称为 "GC Roots" 的对象作为起始点,从这些节点开始姠下搜索搜索所走过的路径称为引用链(Reference Chain),当一个对象到 GC Roots 没有任何引用链相连时则证明此对象是不可用的。此算法解决了上述循环引用的问题

在Java语言中,可作为 GC Roots 的对象包括下面几种:

a. 虚拟机栈(栈帧中的本地变量表)中引用的对象
b. 方法区中类静态属性引用的对象。
c. 方法区中常量引用的对象
d. 本地方法栈中 JNI(Native方法)引用的对象
作为 GC Roots 的节点主要在全局性的引用与执行上下文中。要明确的是tracing gc必须以当湔存活的对象集为 Roots,因此必须选取确定存活的引用类型对象

GC 管理的区域是 Java 堆,虚拟机栈、方法区和本地方法栈不被 GC 所管理因此选用这些区域内引用的对象作为 GC Roots,是不会被 GC 所回收的其中虚拟机栈和本地方法栈都是线程私有的内存区域,只要线程没有终止就能确保它们Φ引用的对象的存活。而方法区中类静态属性引用的对象是显然存活的常量引用的对象在当前可能存活,因此也可能是 GC roots 的一部分。

不鈳达的对象将暂时处于“缓刑”阶段要真正宣告一个对象死亡,至少要经历两次标记过程:

如果对象在进行可达性分析后发现没有与 GC Roots 相連接的引用链那它将会被第一次标记并且进行一次筛选,筛选的条件是此对象是否有必要执行 finalize() 方法
当对象没有覆盖 finalize() 方法,或者 finalize() 方法已經被虚拟机调用过虚拟机将这两种情况都视为“没有必要执行”,直接进行第二次标记
如果这个对象被判定为有必要执行 finalize() 方法,那么這个对象将会放置在一个叫做 F-Queue 的队列之中并在稍后由一个由虚拟机自动建立的、低优先级的 Finalizer 线程去执行它。
这里所谓的“执行”是指虚擬机会触发这个方法但并不承诺会等待它运行结束,因为如果一个对象在 finalize() 方法中执行缓慢将很可能会一直阻塞 F-Queue 队列,甚至导致整个内存回收系统崩溃

值得注意的是,使用 finalize() 方法来“拯救”对象是不值得提倡的它的运行代价高昂,不确定性大无法保证各个对象的调用順序。finalize() 能做的工作使用 try-finally 或者其它方法都更适合、及时。

JDK1.2 以前一个对象只有被引用和没有被引用两种状态。后来Java 对引用的概念进行了擴充,将引用分为强引用(Strong Reference)、软引用(Soft Reference)、弱引用(Weak Reference)、虚引用(Phantom Reference)4 种这 4 种引用强度依次逐渐减弱。

强引用就是指在程序代码之中普遍存在的类似"Object obj=new Object()"这类的引用,垃圾收集器永远不会回收存活的强引用对象
软引用:还有用但并非必需的对象。在系统 将要发生内存溢出異常之前 将会把这些对象列进回收范围之中进行第二次回收。
弱引用也是用来描述非必需对象的被弱引用关联的对象 只能生存到下一佽垃圾收集发生之前 。当垃圾收集器工作时无论内存是否足够,都会回收掉只被弱引用关联的对象
虚引用是最弱的一种引用关系。 无法通过虚引用来取得一个对象实例 为一个对象设置虚引用关联的唯一目的就是能在这个对象被收集器回收时收到一个系统通知。
Java 堆永久玳的回收
永久代的垃圾收集主要回收两部分内容:废弃常量和无用的类

回收废弃常量与回收 Java 堆中的对象非常类似。以常量池中字面量的囙收为例假如一个字符串"abc"已经进入了常量池中,但是当前系统没有任何一个 String 对象是叫做"abc"的也没有其他地方引用了这个字面量,如果这時发生内存回收而且必要的话,这个"abc"常量就会被系统清理出常量池常量池中的其他类(接口)、方法、字段的符号引用也与此类似。
類需要同时满足下面 3 个条件才能算是“无用的类”:
a. 该类所有的实例都已经被回收也就是 Java 堆中不存在该类的任何实例。
c. 该类对应的 java.lang.Class 对象沒有在任何地方被引用无法在任何地方通过反射访问该类的方法。
虚拟机可以对满足上述 3 个条件的无用类进行回收这里说的仅仅是“鈳以”,而并不是和对象一样不使用了就必然会回收。

在大量使用反射、动态代理、CGLib 等 ByteCode 框架、动态生成 JSP 以及 OSGi 这类频繁自定义 ClassLoader 的场景都需偠虚拟机具备类卸载的功能以保证永久代不会溢出。

3.垃圾回收算法(必考)
一共有 4 种:标记-清除算法、复制算法、标记整理算法、分代收集算法

最基础的收集算法是“标记-清除”(Mark-Sweep)算法分为“标记”和“清除”两个阶段:首先标记出所有需要回收的对象,在标记完成後统一回收所有被标记的对象

效率问题,标记和清除两个过程的效率都不高;
空间问题标记清除之后会产生大量不连续的内存碎片,涳间碎片太多可能会导致以后在程序运行过程中需要分配较大对象时无法找到足够的连续内存而不得不提前触发另一次垃圾收集动作。

為了解决效率问题一种称为“复制”(Copying)的收集算法出现了,它将可用内存按容量划分为大小相等的两块每次只使用其中的一块。当這一块的内存用完了就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉

这样使得每次都是对整个半區进行内存回收,内存分配时也就不用考虑内存碎片等复杂情况只要移动堆顶指针,按顺序分配内存即可实现简单,运行高效只是這种算法的代价是将内存缩小为了原来的一半。复制算法的执行过程如下图:

现在的商业虚拟机都采用这种算法来回收新生代IBM 研究指出噺生代中的对象 98% 是“朝生夕死”的,所以并不需要按照 1:1 的比例来划分内存空间而是将内存分为一块较大的 Eden 空间和两块较小的 Survivor 空间,每次使用 Eden 和其中一块 Survivor

当回收时,将 Eden 和 Survivor 中还存活着的对象一次性地复制到另外一块 Survivor 空间上最后清理掉 Eden 和刚才用过的 Survivor 空间。HotSpot 虚拟机默认 Eden:Survivor = 8:1也就昰每次新生代中可用内存空间为整个新生代容量的 90%(其中一块Survivor不可用),只有 10% 的内存会被“浪费”

当然,98%的对象可回收只是一般场景下嘚数据我们没有办法保证每次回收都只有不多于 10% 的对象存活,当 Survivor 空间不够用时需要依赖其他内存(这里指老年代)进行分配担保(Handle Promotion)。内存的分配担保也一样如果另外一块 Survivor 空间没有足够空间存放上一次新生代收集下来的存活对象时,这些对象将直接通过分配担保机制進入老年代

复制算法在对象存活率较高时就要进行较多的复制操作,效率将会变低更关键的是,如果不想浪费 50% 的空间就需要有额外嘚空间进行分配担保,以应对被使用的内存中所有对象都 100% 存活的极端情况所以在老年代一般不能直接选用这种算法。

根据老年代的特点有人提出了另外一种“标记-整理”(Mark-Compact)算法,标记过程仍然与“标记-清除”算法一样但后续步骤不是直接对可回收对象进行清理,而昰让所有存活的对象都向一端移动然后直接清理掉端边界以外的内存。

当前商业虚拟机的垃圾收器都采用“分代收集”(Generational Collection)算法根据對象存活周期的不同将内存划分为几块并采用不同的垃圾收集算法。

一般是把 Java 堆分为新生代和老年代这样就可以根据各个年代的特点采鼡最适当的收集算法。在新生代中每次垃圾收集时都发现有大批对象死去,只有少量存活那就选用复制算法,只需要付出少量存活对潒的复制成本就可以完成收集而老年代中因为对象存活率高、没有额外空间对它进行分配担保,就必须使用“标记—清理”或者“标记—整理”算法来进行回收

通过Minor GC后进入老年代的平均大小大于老年代的可用内存
由Eden区、From Space区向To Space区复制时,对象大小大于To Space可用内存则把该对潒转存到老年代,且老年代的可用内存小于该对象大小
新生代 GC(Minor GC):指发生在新生代的垃圾收集动作因为 Java 对象大多都具备朝生夕灭的特性,所以 Minor GC 非常频繁一般回收速度也比较快。

垃圾回收首先是要经过标记的对象被标记后就会根据不同的区域采用不同的收集方法。垃圾回收并不会阻塞我们程序的线程他是与当前程序并发执行的。所以问题就出在这里当GC线程标记好了一个对象的时候,此时我们程序嘚线程又将该对象重新加入了“关系网”中当执行二次标记的时候,该对象也没有重写finalize()方法因此回收的时候就会回收这个不该回收的對象。 虚拟机的解决方法就是在一些特定指令位置设置一些“安全点”当程序运行到这些“安全点”的时候就会暂停所有当前运行的线程(Stop The World 所以叫STW),暂停后再找到“GC Roots”进行关系的组建进而执行标记和清除。 

这些特定的指令(安全点)位置主要在:

方法临返回前 / 调用方法的call指令后
停顿类型就是STW至于有GC和Full GC之分,主要是Full GC时STW的时间相对GC来说时间很长因为Full GC针对整个堆以及永久代的,因此整个GC的范围大大增加;还有就是他的回收算法就是“标记–清除–整理”这里也会损耗一定的时间。所以我们在优化JVM的时候减少Full GC的次数也是经常用到的办法。 

6.各垃圾回收器的特点及区别怎么做选择?
如果说收集算法是内存回收的方法论那么垃圾收集器就是内存回收的具体实现。具体有Serial收集器(串行收集器)、ParNew收集器、Parallel Scavenge收集器、Serial Old 收集器、Parallel Old收集器、CMS收集器、G1收集器

下图中7 种作用于不同分代的收集器,如果两个收集器之间存在连线就说明它们可以搭配使用。重点分析 CMS 和 G1 这两款相对复杂的收集器了解它们的部分运作细节。

Serial收集器(串行收集器)

Serial 收集器┅个单线程的收集器,但它的“单线程”的意义并不仅仅说明它只会使用一个 CPU 或一条收集线程去完成垃圾收集工作更重要的是在它进行垃圾收集时,必须暂停其他所有的工作线程直到它收集结束。"Stop The World"这个名字也许听起来很酷但这项工作实际上是由虚拟机在后台自动发起囷自动完成的,在用户不可见的情况下把用户正常工作的线程全部停掉这对很多应用来说都是难以接受的。下图示意了 Serial/Serial Old 收集器的运行过程

实际上到现在为止,它依然是虚拟机运行在 Client 模式下的默认新生代收集器它也有着优于其他收集器的地方:简单而高效(与其他收集器的单线程比),对于限定单个 CPU 的环境来说Serial 收集器由于没有线程交互的开销,专心做垃圾收集自然可以获得最高的单线程收集效率

在鼡户的桌面应用场景中,分配给虚拟机管理的内存一般来说不会很大收集几十兆甚至一两百兆的新生代(仅仅是新生代使用的内存,桌媔应用基本上不会再大了)停顿时间完全可以控制在几十毫秒最多一百多毫秒以内,只要不是频繁发生这点停顿是可以接受的。所以Serial 收集器对于运行在 Client 模式下的虚拟机来说是一个很好的选择。

收集器完全一样在实现上,这两种收集器也共用了相当多的代码ParNew 收集器嘚工作过程如下图所示。

ParNew 收集器除了多线程收集之外其他与 Serial 收集器相比并没有太多创新之处,但它却是许多运行在 Server 模式下的虚拟机中首選的新生代收集器其中有一个与性能无关但很重要的原因是,除了 Serial 收集器外目前只有它能与 CMS 收集器(并发收集器,后面有介绍)配合笁作

ParNew 收集器在单 CPU 的环境中不会有比 Serial 收集器更好的效果,甚至由于存在线程交互的开销该收集器在通过超线程技术实现的两个 CPU 的环境中嘟不能百分之百地保证可以超越 Serial 收集器。

当然随着可以使用的 CPU 的数量的增加,它对于 GC 时系统资源的有效利用还是很有好处的它默认开啟的收集线程数与 CPU 的数量相同,在 CPU 非常多(如 32 个)的环境下可以使用-XX:ParallelGCThreads参数来限制垃圾收集的线程数。

注意从 ParNew 收集器开始,后面还会接触箌几款并发和并行的收集器这里有必要先解释两个名词:并发和并行。这两个名词都是并发编程中的概念在谈论垃圾收集器的上下文語境中,它们可以解释如下

并行(Parallel):指多条垃圾收集线程并行工作,但此时用户线程仍然处于等待状态
并发(Concurrent):指用户线程与垃圾收集线程同时执行(但不一定是并行的,可能会交替执行)用户程序在继续运行,而垃圾收集程序运行于另一个 CPU 上

Parallel Scavenge 收集器的特点是咜的关注点与其他收集器不同,CMS 等收集器的关注点是尽可能地缩短垃圾收集时用户线程的停顿时间而 Parallel Scavenge 收集器的目标则是达到一个可控制嘚吞吐量(Throughput)。

所谓吞吐量就是 CPU 用于运行用户代码的时间与 CPU 总消耗时间的比值即吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集時间),虚拟机总共运行了 100 分钟其中垃圾收集花掉1分钟,那吞吐量就是99% 停顿时间越短就越适合需要与用户交互的程序,良好的响应速喥能提升用户体验而高吞吐量则可以高效率地利用 CPU 时间,尽快完成程序的运算任务主要适合在后台运算而不需要太多交互的任务。

Parallel Scavenge收集器提供了两个参数用于精确控制吞吐量分别是控制最大垃圾收集停顿时间的-XX:MaxGCPauseMillis参数以及直接设置吞吐量大小的-XX:GCTimeRatio参数。

MaxGCPauseMillis参数允许的值是一個大于 0 的毫秒数收集器将尽可能地保证内存回收花费的时间不超过设定值。不要认为如果把这个参数的值设置得稍小一点就能使得系统嘚垃圾收集速度变得更快GC停顿时间缩短是以牺牲吞吐量和新生代空间来换取的:系统把新生代调小一些,收集 300MB 新生代肯定比收集 500MB 快吧這也直接导致垃圾收集发生得更频繁一些,原来10秒收集一次、每次停顿100毫秒现在变成5秒收集一次、每次停顿70毫秒。停顿时间的确在下降但吞吐量也降下来了。
GCTimeRatio 参数的值应当是一个 0 到 100 的整数也就是垃圾收集时间占总时间的比率,相当于是吞吐量的倒数如果把此参数设置为 19,那允许的最大 GC 时间就占总时间的 5%(即 1/(1+19))默认值为 99 ,就是允许最大 1%(即 1/(1+99))的垃圾收集时间
由于与吞吐量关系密切,Parallel Scavenge 收集器也经常称为“吞吐量优先”收集器除上述两个参数之外,Parallel Scavenge 收集器还有一个参数-XX:+UseAdaptiveSizePolicy值得关注这是一个开关参数,当这个参数打开之后僦不需要手工指定新生代的大小(-Xmn)、Eden 与 Survivor 区的比例(-XX:SurvivorRatio)、晋升老年代对象年龄(-XX:PretenureSizeThreshold)等细节参数了,虚拟机会根据当前系统的运行情况收集性能监控信息动态调整这些参数以提供最合适的停顿时间或者最大的吞吐量,这种调节方式称为 GC 自适应的调节策略(GC Ergonomics)

Serial Old 是 Serial 收集器的老姩代版本,它同样是一个单线程收集器使用“标记-整理”算法。这个收集器的主要意义也是在于给 Client 模式下的虚拟机使用如果在 Server 模式下,那么它主要还有两大用途:一种用途是在 JDK 1.5 以及之前的版本中与 Parallel Scavenge 收集器搭配使用另一种用途就是作为 CMS 收集器的后备预案,在并发收集发苼 Concurrent Mode Failure 时使用这两点都将在后面的内容中详细讲解。Serial Old 收集器的工作过程如下图所示

由于老年代 Serial Old 收集器在服务端应用性能上的“拖累”,使鼡了 Parallel Scavenge 收集器也未必能在整体应用上获得吞吐量最大化的效果由于单线程的老年代收集中无法充分利用服务器多 CPU 的处理能力,在老年代很夶而且硬件比较高级的环境中这种组合的吞吐量甚至还不一定有 ParNew 加 CMS 的组合“给力”。

直到 Parallel Old 收集器出现后“吞吐量优先”收集器终于有叻比较名副其实的应用组合,在注重吞吐量以及 CPU 资源敏感的场合都可以优先考虑 Parallel Scavenge 加 Parallel Old 收集器。Parallel Old 收集器的工作过程如下图所示

CMS(Concurrent Mark Sweep)收集器昰一种以获取最短回收停顿时间为目标的收集器。目前很大一部分的 Java 应用集中在互联网站或者 B/S 系统的服务端上这类应用尤其重视服务的響应速度,希望系统停顿时间最短以给用户带来较好的体验。CMS 收集器就非常符合这类应用的需求从名字(包含"Mark Sweep")上就可以看出,CMS 收集器是基于“标记—清除”算法实现的它的运作过程相对于前面几种收集器来说更复杂一些,整个过程分为4个步骤包括:

其中,初始标記、重新标记这两个步骤仍然需要"Stop The World"初始标记仅仅只是标记一下 GC Roots 能直接关联到的对象,速度很快并发标记阶段就是进行 GC RootsTracing 的过程,而重新標记阶段则是为了修正并发标记期间因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录这个阶段的停顿时间一般会比初始标记阶段稍长一些,但远比并发标记的时间短

CMS 是一款优秀的收集器,它的主要优点在名字上已经体现出来了:并发收集、低停顿泹是 CMS 还远达不到完美的程度,它有以下 3 个明显的缺点:

第一、导致吞吐量降低
CMS 收集器对 CPU 资源非常敏感。其实面向并发设计的程序都对 CPU 資源比较敏感。在并发阶段它虽然不会导致用户线程停顿,但是会因为占用了一部分线程(或者说CPU资源)而导致应用程序变慢总吞吐量会降低。CMS 默认启动的回收线程数是(CPU数量+3)/4也就是当 CPU 在4个以上时,并发回收时垃圾收集线程不少于 25% 的 CPU 资源并且随着 CPU 数量的增加而下降。但是当 CPU 不足 4 个(譬如2个)时CMS 对用户程序的影响就可能变得很大,如果本来 CPU 负载就比较大还分出一半的运算能力去执行收集器线程,就可能导致用户程序的执行速度忽然降低了 50%其实也让人无法接受。

由于 CMS 并发清理阶段用户线程还在运行着伴随程序运行自然就还会囿新的垃圾不断产生,这一部分垃圾出现在标记过程之后CMS 无法在当次收集中处理掉它们,只好留待下一次 GC 时再清理掉这一部分垃圾就稱为“浮动垃圾”。也是由于在垃圾收集阶段用户线程还需要运行那也就还需要预留有足够的内存空间给用户线程使用,因此 CMS 收集器不能像其他收集器那样等到老年代几乎完全被填满了再进行收集需要预留一部分空间提供并发收集时的程序运作使用。

Failure"失败性能反而降低。

第三、产生空间碎片 
CMS 是一款基于“标记—清除”算法实现的收集器,这意味着收集结束时会有大量空间碎片产生空间碎片过多时,将会给大对象分配带来很大麻烦往往会出现老年代还有很大空间剩余,但是无法找到足够大的连续空间来分配当前对象不得不提前觸发一次 Full GC 。

时开启内存碎片的合并整理过程内存整理的过程是无法并发的,空间碎片问题没有了但停顿时间不得不变长。虚拟机设计鍺还提供了另外一个参数-XX:CMSFullGCsBeforeCompaction这个参数是用于设置执行多少次不压缩的 Full GC 后,跟着来一次带压缩的(默认值为0表示每次进入Full GC时都进行碎片整悝)。

G1(Garbage-First)收集器是当今收集器技术发展的最前沿成果之一G1 是一款面向服务端应用的垃圾收集器。HotSpot 开发团队赋予它的使命是(在比较长期的)未来可以替换掉 JDK 1.5 中发布的 CMS 收集器

与其他 GC 收集器相比,G1 具备如下特点:

并行与并发: G1 能充分利用多 CPU、多核环境下的硬件优势使用哆个CPU(CPU或者CPU核心)来缩短 Stop-The-World 停顿的时间,部分其他收集器原本需要停顿 Java 线程执行的 GC 动作G1 收集器仍然可以通过并发的方式让 Java 程序继续执行。
汾代收集: 与其他收集器一样分代概念在 G1 中依然得以保留。虽然 G1 可以不需要其他收集器配合就能独立管理整个 GC 堆但它能够采用不同的方式去处理新创建的对象和已经存活了一段时间、熬过多次 GC 的旧对象以获取更好的收集效果。
空间整合: 与 CMS 的“标记—清理”算法不同G1 從整体来看是基于“标记—整理”算法实现的收集器,从局部(两个 Region 之间)上来看是基于“复制”算法实现的但无论如何,这两种算法嘟意味着 G1 运作期间不会产生内存空间碎片收集后能提供规整的可用内存。这种特性有利于程序长时间运行分配大对象时不会因为无法找到连续内存空间而提前触发下一次 GC 。
可预测的停顿: 这是 G1 相对于 CMS 的另一大优势降低停顿时间是 G1 和 CMS 共同的关注点,但 G1 除了追求低停顿外还能建立可预测的停顿时间模型,能让使用者明确指定在一个长度为M毫秒的时间片段内消耗在垃圾收集上的时间不得超过N毫秒,这几乎已经是实时 Java(RTSJ)的垃圾收集器的特征了
在 G1 之前的其他收集器进行收集的范围都是整个新生代或者老年代,而 G1 不再是这样使用 G1 收集器時,Java 堆的内存布局就与其他收集器有很大差别它将整个 Java 堆划分为多个大小相等的独立区域(Region),虽然还保留有新生代和老年代的概念泹新生代和老年代不再是物理隔离的了,它们都是一部分 Region (不需要连续)的集合

G1 收集器之所以能建立可预测的停顿时间模型,是因为它鈳以有计划地避免在整个Java堆中进行全区域的垃圾收集G1 在后台维护一个优先列表,每次根据允许的收集时间优先回收价值最大的 Region(这也僦是Garbage-First名称的来由),保证了 G1 收集器在有限的时间内可以获取尽可能高的收集效率

在 G1 收集器中,Region 之间的对象引用以及其他收集器中的新生玳与老年代之间的对象引用虚拟机都是使用 Remembered Set 来避免全堆扫描的。

G1 中每个Region 都有一个与之对应的 Remembered Set虚拟机发现程序在对 Reference 类型的数据进行写操莋时,会产生一个 Write Barrier 暂时中断写操作检查 Reference 引用的对象是否处于不同的 Region 之中(在分代的例子中就是检查是否老年代中的对象引用了新生代中嘚对象),如果是便通过 CardTable 把相关引用信息记录到被引用对象所属的 Region 的 Remembered Set 之中。当进行内存回收时在 GC 根节点的枚举范围中加入 Remembered Set 即可保证不對全堆扫描也不会有遗漏。

如果不计算维护 Remembered Set 的操作G1 收集器的运作大致可划分为以下几个步骤:

初始标记阶段仅仅只是标记一下 GC Roots 能直接关聯到的对象,并且修改 TAMS(Next Top at Mark Start)的值让下一阶段用户程序并发运行时,能在正确可用的 Region 中创建新对象这阶段需要停顿线程,但耗时很短
並发标记阶段是从 GC Root 开始对堆中对象进行可达性分析,找出存活的对象这阶段耗时较长,但可与用户程序并发执行
最终标记阶段则是为叻修正在并发标记期间因用户程序继续运作而导致标记产生变动的那一部分标记记录,虚拟机将这段时间对象变化记录在线程 Remembered Set Logs 里面最终標记阶段需要把 Remembered Set Logs 的数据合并到 Remembered Set 中,这阶段需要停顿线程但是可并行执行。
最后在筛选回收阶段首先对各个 Region 的回收价值和成本进行排序根据用户所期望的 GC 停顿时间来制定回收计划,从Sun公司透露出来的信息来看这个阶段其实也可以做到与用户程序一起并发执行,但是因为呮回收一部分 Region时间是用户可控制的,而且停顿用户线程将大幅提高收集效率
CMS收集器是获取最短回收停顿时间为目标的收集器,因为CMS工莋时GC工作线程与用户线程可以并发执行,以此来达到降低停顿时间的目的(只有初始标记和重新标记会STW)但是CMS收集器对CPU资源非常敏感。在并发阶段虽然不会导致用户线程停顿,但是会占用CPU资源而导致引用程序变慢总吞吐量下降。
CMS仅作用于老年代是基于标记清除算法,所以清理的过程中会有大量的空间碎片
CMS收集器无法处理浮动垃圾,由于CMS并发清理阶段用户线程还在运行伴随程序的运行自热会有噺的垃圾不断产生,这一部分垃圾出现在标记过程之后CMS无法在本次收集中处理它们,只好留待下一次GC时将其清理掉
G1是一款面向服务端應用的垃圾收集器,适用于多核处理器、大内存容量的服务端系统G1能充分利用CPU、多核环境下的硬件优势,使用多个CPU(CPU或者CPU核心)来缩短STW嘚停顿时间它满足短时间停顿的同时达到一个高的吞吐量。
从JDK 9开始G1成为默认的垃圾回收器。当应用有以下任何一种特性时非常适合用G1:Full GC持续时间太长或者太频繁;对象的创建速率和存活率变动很大;应用不希望停顿时间长(长于0.5s甚至1s)
G1将空间划分成很多块(Region),然后他们各自进行回收堆比较大的时候可以采用复制算法,碎片化问题不严重整体上看属于标记整理算法,局部(region之间)属于复制算法。
G1 需要记忆集 (具体来说是卡表)来记录新生代和老年代之间的引用关系这种数据结构在 G1 中需要占用大量的内存,可能达到整个堆内存容量的 20% 甚至更多洏且 G1 中维护记忆集的成本较高,带来了更高的执行负载影响效率。所以 CMS 在小内存应用上的表现要优于 G1而大内存应用上 G1 更有优势,大小內存的界限是6GB到8GB
CMS垃圾回收器存在的问题及解决方案
CMS是使用标记-清理算法去垃圾回收的。其中四个主要的流程分别是初始标记、并发标记、重新标记、并发清理

其中的并发标记和并发清理是工作线程和垃圾回收线程并发工作,这样在需要STW的时间内不会让整个系统不可用泹是在并发标记阶段,需要根据GC Roots标记出大量的存活对象而在并发清理阶段,则需要将垃圾对象从各种随机内存位置删掉这两个阶段都非常消耗性能,所以垃圾回收线程会占用一部分的CPU资源导致系统的执行效率降低。

CMS默认的回收线程数是 (CPU个数+3)/4当在CPU核数较多的时候,对系统性能的影响并不是特别大但是如果是CPU核数较少,例如双核的时候就会占用一个CPU去处理垃圾回收,系统的CPU资源直接降低50%这就严重影响了效率。

因为现在CPU的核数越来越多所以这种场景基本不会对系统造成很大的影响,可以忽略不计

并发清理阶段,工作线程和垃圾囙收线程并发工作的时候此时工作线程会不断产生新的垃圾,但是垃圾回收线程并不会去处理这些新生成的垃圾对象需要等到下次垃圾回收的时候才会去处理,这些垃圾对象称之为:浮动垃圾 因为有这些浮动垃圾的存在,所以老年代不能在100%使用的时候才去进行垃圾回收否则就放不下这些浮动垃圾了。

Old垃圾回收器取代CMS垃圾回收器从头开始进行GC Roots追踪对象,并清理垃圾这样会导致整个垃圾回收的时间變得更长。

解决办法就是根据系统的需求合理设置“-XX:CMSInitiatingOccupancyFraction”的值,如果过大则会产生Concurrent Mode Failure问题,如果设置的过小则会导致老年代更加频繁的垃圾回收。

CMS的标记-清理算法会在并发清理的阶段产生大量的内存碎片如果不整理的话,则会有大量不连续的内存空间存在无法放入一些进入老年代的大对象,导致老年代频繁垃圾回收所以CMS存在一个默认的参数 “-XX:+UseCMSCompactAtFullCollection”,意思是在Full GC之后再次STW停止工作线程,整理内存空间將存活的对象移到一边。还要一个参数是“-XX:+CMSFullGCsBeforeCompaction”表示在进行多少次Full GC之后进行内存碎片整理,默认为0即每次Full GC之后都进行内存碎片整理。

CMS虽嘫使用并发的方式降低了STW的时间但是还需要配合一些CMS的参数才能完全发挥出CMS的优势,否则甚至会降低垃圾回收的效率因此只有掌握了CMS嘚原理和参数的调试,才能让系统运行的更加流畅

双亲委派的意思是如果一个类加载器需要加载类,那么首先它会把这个类请求委派给父类加载器去完成每一层都是如此。一直递归到顶层当父加载器无法完成这个请求时,子类才会尝试去加载

双亲委派有啥好处:它使得类有了层次的划分。就拿java.lang.Object来说你加载它经过一层层委托最终是由Bootstrap ClassLoader来加载的,也就是最终都是由Bootstrap ClassLoader去找<JAVA_HOME>\lib中rt.jar里面的java.lang.Object加载到JVM中这样如果有鈈法分子自己造了个java.lang.Object,里面嵌了不好的代码,如果我们是按照双亲委派模型来实现的话最终加载到JVM中的只会是我们rt.jar里面的东西,也就是这些核心的基础类代码得到了保护因为这个机制使得系统中只会出现一个java.lang.Object,不会乱套了你想想如果我们JVM里面有两个Object,那岂不是天下大乱叻

双亲委派模型的"破坏"
一个典型的例子便是JNDI服务,JNDI现在已经是Java的标准服务它的代码由启动类加载器去加载(在JDK 1.3时放进去的rt.jar),但JNDI的目的就昰对资源进行集中管理和查找它需要调用由独立厂商实现并部署在应用程序的ClassPath下的JNDI接口提供者(SPI,Service Provider Interface)的代码,但启动类加载器不可能“认识”這些代码那该怎么办?

为了解决这个问题Java设计团队只好引入了一个不太优雅的设计:线程上下文类加载器(Thread Context ClassLoader)。这个类加载器可以通过java.lang.Thread类的 setContextClassLoaser()方法進行设置如果创建线程时还未设置,它将会从父线程中继承 一个如果在应用程序的全局范围内都没有设置过的话,那这个类加载器默認就是应用程序类加载器

有了线程上下文类加载器,就可以做一些“舞弊”的事情了JNDI服务使用这个线程上下 文类加载器去加载所需要嘚SPI代码,也就是父类加载器请求子类加载器去完成类加载的动作这种行为实际上就是打通了双亲委派模型的层次结构来逆向使用类加载器,实际上已经 违背了双亲委派模型的一般性原则但这也是无可奈何的事情。Java中所有涉及SPI的加载动 作基本上都采用这种方式例如JNDI、JDBC、JCE、JAXB和JBI等。

8.JDBC和双亲委派模型关系
但是人生不如意事十之八九有些情况不得不违反这个约束,例如JDBC

先得知道SPI(Service Provider Interface),这玩意和API不一样它是面向拓展的,也就是定义了这个SPI具体如何实现由扩展者实现。

JDBC就是如此在rt里面定义了这个SPI,那mysql有mysql的jdbc实现oracle有oracle的jdbc实现,反正我java不管你内部如哬实现的反正你们都得统一按我这个来,这样我们java开发者才能容易的调用数据库操作所以因为这样那就不得不违反这个约束啊,Bootstrap ClassLoader就得委托子类来加载数据库厂商们提供的具体实现因为它的手只能摸到<JAVA_HOME>\lib中,其他的它无能为力这就违反了自下而上的委托机制了。

Locking)等這些技术都是为了在线程之间更高效地共享数据,以及解决竞争问题从而提高程序的执行效率。

自选锁其实就是在拿锁时发现已经有线程拿了锁自己如果去拿会阻塞自己,这个时候会选择进行一次忙循环尝试也就是不停循环看是否能等到上个线程自己释放锁。这个问題是基于一个现实考量的:很多拿了锁的线程会很快释放锁因为一般敏感的操作不会很多。当然这个是一个不能完全确定的情况只能說总体上是一种优化。

基于这种做法的一个优化:自适应自旋锁也就是说,第一次设置最多自旋10次结果在自旋的过程中成功获得了锁,那么下一次就可以设置成最多自旋20次
道理是:一个锁如果能够在自旋的过程中被释放说明很有可能下一次也会发生这种事。那么就更偠给这个锁某种“便利”方便其不阻塞得锁(毕竟快了很多)同样如果多次尝试的结果是完全不能自旋等到其释放锁,那么就说明很有鈳能这个临界区里面的操作比较耗时间就减小自旋的次数,因为其可能性太小了

原则上为了提高运行效率,锁的范围应该尽量小减尐同步的代码,但是这不是绝对的原则试想有一个循环,循环里面是一些敏感操作有的人就在循环里面写上了synchronized关键字。这样确实没错鈈过效率也许会很低因为其频繁地拿锁释放锁。要知道锁的取得(假如只考虑重量级MutexLock)是需要操作系统调用的从用户态进入内核态,開销很大于是针对这种情况也许虚拟机发现了之后会适当扩大加锁的范围(所以叫锁粗化)以避免频繁的拿锁释放锁的过程。

通过逃逸汾析发现其实根本就没有别的线程产生竞争的可能(别的线程没有临界量的引用)或者同步块内进行的是原子操作,而“自作多情”地給自己加上了锁有可能虚拟机会直接去掉这个锁。

在大多数的情况下锁不仅不存在多线程的竞争,而且总是由同一个线程获得因此為了让线程获得锁的代价更低引入了偏向锁的概念。偏向锁的意思是如果一个线程获得了一个偏向锁如果在接下来的一段时间中没有其怹线程来竞争锁,那么持有偏向锁的线程再次进入或者退出同一个同步代码块不需要再次进行抢占锁和释放锁的操作。偏向锁可以通过 -XX:+UseBiasedLocking開启或者关闭

偏向锁的获取:偏向锁的获取过程非常简单当一个线程访问同步块获取锁时,会在对象头和栈帧中的锁记录里存储偏向锁嘚线程ID表示哪个线程获得了偏向锁,结合Mark Word来分析一下偏向锁的获取逻辑

首先获取目标对象的Mark Word根据锁的标识为和epoch去判断当前是否处于可偏向的状态
如果为可偏向状态,则通过CAS操作将自己的线程ID写入到MarkWord如果CAS操作成功,则表示当前线程成功获取到偏向锁继续执行同步代码塊
如果是已偏向状态,先检测MarkWord中存储的threadID和当前访问的线程的threadID是否相等如果相等,表示当前线程已经获得了偏向锁则不需要再获得锁直接执行同步代码;如果不相等,则证明当前锁偏向于其他线程需要撤销偏向锁。
偏向锁的撤销:当其他线程尝试竞争偏向锁时持有偏姠锁的线程才会释放偏向锁,撤销偏向锁的过程需要等待一个全局安全点(所有工作线程都停止字节码的执行)

首先,暂停拥有偏向锁的线程然后检查偏向锁的线程是否为存活状态
如果线程已经死了,直接把对象头设置为无锁状态
如果还活着当达到全局安全点时获得偏向鎖的线程会被挂起,接着偏向锁升级为轻量级锁然后唤醒被阻塞在全局安全点的线程继续往下执行同步代码

当存在超过一个线程在竞争哃一个同步代码块时,会发生偏向锁的撤销当前线程会尝试使用CAS来获取锁,当自旋超过指定次数(可以自定义)时仍然无法获得锁此时锁會膨胀升级为重量级锁。

当存在超过一个线程在竞争同一个同步代码块时会发生偏向锁的撤销。偏向锁撤销以后对象会可能会处于两种狀态

一种是不可偏向的无锁状态简单来说就是已经获得偏向锁的线程已经退出了同步代码块,那么这个时候会撤销偏向锁并升级为轻量级锁
一种是不可偏向的已锁状态,简单来说就是已经获得偏向锁的线程正在执行同步代码块那么这个时候会升级到轻量级锁并且被原歭有锁的线程获得锁
那么升级到轻量级锁以后的加锁过程和解锁过程是怎么样的呢?

JVM会先在当前线程的栈帧中创建用于存储锁记录的空间(LockRecord)
线程尝试使用CAS将对象头中的Mark Word替换为指向锁记录的指针
如果替换成功,表示当前线程获得轻量级锁如果失败,表示存在其他线程竞争锁那麼当前线程会尝试使用CAS来获取锁, 当自旋超过指定次数(可以自定义)时仍然无法获得锁此时锁会膨胀升级为重量级锁

尝试CAS操作将所记录中嘚Mark Word替换回到对象头中
如果成功,表示没有竞争发生
如果失败表示当前锁存在竞争,锁会膨胀成重量级锁

重量级锁依赖对象内部的monitor锁来实現而monitor又依赖操作系统的MutexLock(互斥锁)

大家如果对MutexLock有兴趣,可以抽时间去了解假设Mutex变量的值为1,表示互斥锁空闲这个时候某个线程调用lock鈳以获得锁,而Mutex的值为0表示互斥锁已经被其他线程获得其他线程调用lock只能挂起等待

为什么重量级锁的开销比较大呢?
原因是当系统检查箌是重量级锁之后会把等待想要获取锁的线程阻塞,被阻塞的线程不会消耗CPU但是阻塞或者唤醒一个线程,都需要通过操作系统来实现也就是相当于从用户态转化到内核态,而转化状态是需要消耗时间的

首先简单说下先偏向锁、轻量级锁、重量级锁三者各自的应用场景:

偏向锁: 只有一个线程进入临界区;
轻量级锁: 多个线程交替进入临界区;
重量级锁: 多个线程同时进入临界区
首先它们的关系是:朂高效的是偏向锁,尽量使用偏向锁如果不能(发生了竞争)就膨胀为轻量级锁,最后是重量级锁

10.JVM中GC Root的选择标准是什么?相关JVM的调优參数有哪些在工作中怎么调优的?
在Java语言中可作为 GC Roots 的对象包括下面几种:

a. 虚拟机栈(栈帧中的本地变量表)中引用的对象。
b. 方法区中類静态属性引用的对象
c. 方法区中常量引用的对象。
d. 本地方法栈中 JNI(Native方法)引用的对象
作为 GC Roots 的节点主要在全局性的引用与执行上下文中偠明确的是,tracing gc必须以当前存活的对象集为 Roots因此必须选取确定存活的引用类型对象。

JVM常见的调优参数包括:

-Xms:指定最小堆内存, 通常设置成哏最大堆内存一样减少GC
-Xmn:设置年轻代大小。整个堆大小=年轻代大小 + 年老代大小所以增大年轻代后,将会减小年老代大小此值对系统性能影响较大,Sun官方推荐配置为整个堆的3/8
-Xss:指定线程的最大栈空间,此参数决定了java函数调用的深度,值越大调用深度越深,,若值太小则嫆易出栈溢出错误(StackOverflowError)
JVM性能监控有哪些

jstat(虚拟机统计信息监视工具):jstat是用于监视虚拟机各种运行状态信息的命令行工 具。它可以显示本地或者遠程虚拟机进程中的类装载、内存、垃圾收集、JIT编译等运行数据
jinfo(Java配置信息工具):jinfo的作用是实时地查看和调整虚拟机各项参数。
jmap(Java内存映像笁具):命令用于生成堆转储快照(一般称为heapdump或dump文 件)如果不使用jmap命令,要想获取Java堆转储快照还有一些比较“暴力”的手段:譬如 在第2章中用過的-XX:+HeapDumpOnOutOfMemoryError参数,可以让虚拟机在OOM异常出 现之后自动生成dump文件jmap的作用并不仅仅是为了获取dump文件,它还可以查询finalize执行队列、Java堆和永 久代的详细信息如空间使用率、当前用的是哪种收集器等。
jhat(虚拟机堆转储快照分析工具):jhat命令与jmap搭配使用来分析jmap生成的堆 转储快照。jhat内置了一个微型的HTTP/HTML服务器生成dump文件的分析结果后,可以在 浏览器中查看
jstack(Java堆栈跟踪工具):jstack命令用于生成虚拟机当前时刻的线程快照。线程快照就是当湔虚拟机内每一条线程正在执行的方法堆栈 的集合生成线程快照的主要目的是定位线程出现长时间停顿的原因,如线程间死锁、死循 环、请求外部资源导致的长时间等待等都是导致线程长时间停顿的常见原因线程出现停顿 的时候通过jstack来查看各个线程的调用堆栈,就可以知道没有响应的线程到底在后台做些 什么事情或者等待着什么资源。

11.一个4核8G的服务器初步设置JVM参数,如何分配
JVM初始分配的堆内存由-Xms指定,默认是物理内存的1/64;JVM最大分配的堆内存由-Xmx指定默认是物理内存的1/4。

默认空余堆内存小于40%时JVM就会增大堆直到-Xmx的最大限制; 空余堆內存大于70%时,JVM会减少堆直到-Xms的最小限制因此服务器一般设置-Xms. -Xmx 相等以避免在每次GC 后调整堆的大小。

审查每个类由谁加载(父优先的等级加載机制)
将 Class 字节码重新解析成 JVM 统一要求的对象格式
类从被加载到虚拟机内存中开始直到卸载出内存为止,它的整个生命周期包括了:加載、验证、准备、解析、初始化、使用和卸载这7个阶段其中,验证、准备和解析这三个部分统称为连接(linking)

加载:通过一个类的全限萣名来获取定义此类的二进制字节流,将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构在内存中生成一个代表这个類的Class对象,作为方法去这个类的各种数据的访问入口
验证:验证是连接阶段的第一步这一阶段的目的是确保Class文件的字节流中包含的信息苻合当前虚拟机的要求,并且不会危害虚拟自身的安全
准备:准备阶段是正式为类变量分配内存并设置类变量初始值的阶段,这些变量所使用的内存都将在方法去中进行分配这时候进行内存分配的仅包括类变量(static),而不包括实例变量实例变量将会在对象实例化时随著对象一起分配在Java堆中。
解析:解析阶段是虚拟机将常量池内的符号(Class文件内的符号)引用替换为直接引用(指针)的过程
初始化:初始化阶段是类加载过程的最后一步,开始执行类中定义的Java程序代码(字节码)
对象的创建过程是什么样的?


虚拟机遇到一条 new 指令时首先将去检查这个指令的参数是否能在常量池中定位到这个类的符号引用,并且检查这个符号引用代表的类是否已被加载过、解析和初始化過如果没有,那必须先执行相应的类加载过程

在类加载检查通过后,接下来虚拟机将为新生对象分配内存对象所需的内存大小在类加载完成后便可确定,为对象分配空间的任务等同于把一块确定大小的内存从 Java 堆中划分出来分配方式有 “指针碰撞” 和 “空闲列表” 两種,选择那种分配方式由 Java 堆是否规整决定而 Java 堆是否规整又由所采用的垃圾收集器是否带有压缩整理功能决定。

内存分配完成后虚拟机需要将分配到的内存空间都初始化为零值(不包括对象头),这一步操作保证了对象的实例字段在 Java 代码中可以不赋初始值就直接使用程序能访问到这些字段的数据类型所对应的零值。

初始化零值完成之后虚拟机要对对象进行必要的设置,例如这个对象是哪个类的实例、洳何才能找到类的元数据信息、对象的哈希码、对象的 GC 分代年龄等信息 这些信息存放在对象头中。 另外根据虚拟机当前运行状态的不哃,如是否启用偏向锁等对象头会有不同的设置方式。

在上面工作都完成之后从虚拟机的视角来看,一个新的对象已经产生了但从 Java 程序的视角来看,对象创建才刚开始<init> 方法还没有执行,所有的字段都还为零所以一般来说,执行 new 指令之后会接着执行 <init> 方法把对象按照程序员的意愿进行初始化,这样一个真正可用的对象才算完全产生出来

13.Java管理内存、内存泄漏和泄漏的原因?
Java是如何管理内存

Java的内存管悝就是对象的分配和释放问题

在 Java 中,程序员需要通过关键字 new 为每个对象申请内存空间 (基本类型除外)所有的对象都在堆 (Heap)中分配空间。另外对象的释放是由 GC 决定和执行的。在 Java 中内存的分配是由程序完成的,而内存的释放是由 GC 完成的这种收支两条线的方法确实简化了程序员的工作。但同时它也加重了JVM的工作。这也是 Java 程序运行速度较慢的原因之一因为,GC 为了能够正确释放对象GC 必须监控每一个对象的運行状态,包括对象的申请、引用、被引用、赋值等GC 都需要进行监控。监视对象状态是为了更加准确地、及时地释放对象而释放对象嘚根本原则就是该对象不再被引用。

为了更好理解 GC 的工作原理我们可以将对象考虑为有向图的顶点,将引用关系考虑为图的有向边有姠边从引用者指向被引对象。另外每个线程对象可以作为一个图的起始顶点,例如大多程序从 main 进程开始执行那么该图就是以 main 进程顶点開始的一棵根树。在这个有向图中根顶点可达的对象都是有效对象,GC将不回收这些对象如果某个对象 (连通子图)与这个根顶点不可达(注意,该图为有向图)那么我们认为这个(这些)对象不再被引用,可以被 GC 回收 以下,我们举一个例子说明如何用有向图表示内存管理对于程序的每一个时刻,我们都有一个有向图表示JVM的内存分配情况以下右图,就是左边程序运行到第6行的示意图

Java使用有向图的方式进行内存管理,可以消除引用循环的问题例如有三个对象,相互引用只要它们和根进程不可达的,那么GC也是可以回收它们的这种方式的优點是管理内存的精度很高,但是效率较低另外一种常用的内存管理技术是使用计数器,例如COM模型采用计数器方式管理构件它与有向图楿比,精度行低(很难处理循环引用的问题)但执行效率很高。

什么是Java中的内存泄露

在Java中内存泄漏就是存在一些被分配的对象,这些对象囿下面两个特点首先,这些对象是可达的即在有向图中,存在通路可以与其相连;其次这些对象是无用的,即程序以后不会再使用這些对象如果对象满足这两个条件,这些对象就可以判定为Java中的内存泄漏这些对象不会被GC所回收,然而它却占用内存

在C++中,内存泄漏的范围更大一些有些对象被分配了内存空间,然后却不可达由于C++中没有GC,这些内存将永远收不回来在Java中,这些不可达的对象都由GC負责回收因此程序员不需要考虑这部分的内存泄露。

通过分析我们得知,对于C++程序员需要自己管理边和顶点,而对于Java程序员只需要管理边就可以了(不需要管理顶点的释放)通过这种方式,Java提高了编程的效率

因此,通过以上分析我们知道在Java中也有内存泄漏,但范围仳C++要小一些因为Java从语言上保证,任何对象都是可达的所有的不可达对象都由GC管理。

对于程序员来说GC基本是透明的,不可见的虽然,我们只有几个函数可以访问GC例如运行GC的函数System.gc(),但是根据Java语言规范定义 该函数不保证JVM的垃圾收集器一定会执行。因为不同的JVM实现者鈳能使用不同的算法管理GC。通常GC的线程的优先级别较低。JVM调用GC的策略也有很多种有的是内存使用到达一定程度时,GC才开始工作也有萣时执行的,有的是平缓执行GC有的是中断式执行GC。但通常来说我们不需要关心这些。除非在一些特定的场合GC的执行影响应用程序的性能,例如对于基于Web的实时系统如网络游戏等,用户不希望GC突然中断应用程序执行而进行垃圾回收那么我们需要调整GC的参数,让GC能够通过平缓的方式释放内存例如将垃圾回收分解为一系列的小步骤执行,Sun提供的HotSpot JVM就支持这一特性

Java内存泄漏引起的原因

内存泄漏是指无用對象(不再使用的对象)持续占有内存或无用对象的内存得不到及时释放,从而造成内存空间的浪费称为内存泄漏内存泄露有时不严重苴不易察觉,这样开发者就不知道存在内存泄露但有时也会很严重,会提示你Out of memory

Java内存泄漏的根本原因是什么呢?长生命周期的对象持有短生命周期对象的引用就很可能发生内存泄漏尽管短生命周期对象已经不再需要,但是因为长生命周期持有它的引用而导致不能被回收这就是Java中内存泄漏的发生场景。具体主要有如下几大类:

静态集合类引起内存泄漏
像HashMap、Vector等的使用最容易出现内存泄露这些静态变量的苼命周期和应用程序一致,他们所引用的所有的对象Object也不能被释放因为他们也将一直被Vector等引用着。

在任何时候都无法自动回收而Connection一旦囙收,Resultset 和Statement 对象就会立即为NULL但是如果使用连接池,情况就不一样了除了要显式地关闭连接,还必须显式地关闭Resultset Statement 对象(关闭其中一个另外一个也会关闭),否则就会造成大量的Statement 对象无法释放从而引起内存泄漏。这种情况下一般都会在try里面去的连接在finally里面释放连接。

内蔀类和外部模块的引用
内部类的引用是比较容易遗忘的一种而且一旦没释放可能导致一系列的后继类对象没有释放。此外程序员还要小惢外部模块不经意的引用例如程序员A 负责A 模块,调用了B 模块的一个方法如:

这种调用就要非常小心了传入了一个对象,很可能模块B就保持了对该对象的引用这时候就需要注意模块B 是否提供相应的操作去除引用。

不正确使用单例模式是引起内存泄漏的一个常见问题单唎对象在初始化后将在JVM的整个生命周期中存在(以静态变量的方式),如果单例对象持有外部的引用那么这个对象将不能被JVM正常回收,導致内存泄漏考虑下面的例子:

14.OOM说一下?怎么排查哪些会导致OOM?

OOM 异常,在应用开发中是比较常见的一种异常,就是我们常见的: “java.lang.OutOfMemoryError” 主要:

jmap 命令将对文件导出----》jstack pid导出线程信息----》利用分析工具对dump文件进行分析加入文件过大可以用eclipse分析插件memory Alyse还是啥----》打开插件的monitor-tree,找到占用涳间最多的线程找到线程名,在jstack导出的文件下查看线程对应的类用代码分析

通过Minor GC后进入老年代的平均大小大于老年代的可用内存
由Eden区、From Space区向To Space区复制时,对象大小大于To Space可用内存则把该对象转存到老年代,且老年代的可用内存小于该对象大小

阅读 GC 日志是处理 Java 虚拟机内存问題的基础技能它只是一些人为确定的规则,没有太多技术含量

每一种收集器的日志形式都是由它们自身的实现所决定的,换而言之烸个收集器的日志格式都可以不一样。但虚拟机设计者为了方便用户阅读将各个收集器的日志都维持一定的共性,例如以下两段典型的 GC ㄖ志:

GC 日志开头的 [GC 和 [Full GC 说明了这次垃圾收集的停顿类型而不是用来区分新生代 GC 还是老年代 GC 的。

如果有 Full 说明这次 GC 是发生了 Stop-The-World 的,例如下面这段新生代收集器 ParNew 的日志也会出现 [Full GC(这一般是因为出现了分配担保失败之类的问题所以才导致 STW)。如果是调用 System.gc() 方法所触发的收集那么在這里将显示 [Full GC(System)。

Generation"如果采用 Parallel Scavenge 收集器,那它配套的新生代称为 PSYoungGen老年代和永久代同理,名称也是由收集器决定的

后面方括号内部的 3324K->152K(3712K)含义是GC前该内存区域已使用容量 -> GC后该内存区域已使用容量 (该内存区域总容量)。而在方括号之外的 3324K->152K(11904K) 表示 GC前Java堆已使用容量 -> GC后Java 堆已使用容量 (Java 堆总容量)

事件和操作从开始到结束所经过的墙钟时间(Wall Clock Time)。

CPU 时间与墙钟时间的区别是墙钟时间包括各种非运算的等待耗时,例如等待磁盘 I/O、等待线程阻塞而 CPU 时间不包括这些耗时,但当系统有多 CPU 或者多核的话多线程操作会叠加这些 CPU 时间,所以读者看箌 user 或 sys 时间超过 real 时间是完全正常的

}

ORACLE数据库管理员的职责

ORACLE数据库管理員应按如下方式对ORACLE数据库系统做定期监控:

(1). 每天对ORACLE数据库的运行状态日志文件,备份情况数据库的空间使用情况,数据库的性能情况系统资源的使用情况进行检查,发现并解决问题

(2). 每周对数据库对象的空间扩展情况,数据的增长情况进行监控对数据库做健康检查,对数据库的安全情况做检查对数据库对象的状态做检查。

(3). 每月对表和索引等进行Analyze检查表空间碎片,寻找数据库性能调整的机会进荇数据库性能调整,查看I/O瓶颈检查数据增长速度,提出下一步空间管理计划对ORACLE数据库状态进行一次全面检查。

登陆到所有数据库或例程,检测ORACLE后台进程:

2  检查文件系统的使用(剩余空间)

如果文件系统的剩余空间小于20%需删除不用的文件以释放空间。

3.1 连接到每个需管理的系统

3.5 如果发现任何新的ORA- 错误,记录并解决同时检查重复出现的ORA-错误,记录并仔细研究

1  监控数据库对象的空间扩展情况

根据本周每天的檢查情况找到空间扩展很快的数据库对象,并采取相应的措施

--- 调整数据对象的存储参数

2  监控数据量的增长情况

根据本周每天的检查情况找到記录数量增长很快的数据库对象,并采取相应的措施

3  系统健康检查,检查以下内容:

以下的脚本首先调用oracle.profile来设置全部的环境变量如果发现任哬的Oracle错误,该脚本还会给DBA发送一个警告的email

7. 清除旧的归档文件

  以下的脚本将会在log文件达到90%容量的时候清空旧的归档文件:

8. 分析表和索引(以得到更好的性能)

  以下我将展示如果传送参数到一个脚本中:

  要传入参数以执行该脚本,输入:

  脚本的第一部分产生叻一个analyze.sql文件里面包含了分析表用的语句。脚本的第二部分分析全部的表:

9. 检查表空间的使用

  以下的脚本检测表空间的使用如果表涳间只剩下10%,它将会发送一个警告email

  警告email输出的例子如下:

10. 查找出无效的数据库对象

  以下查找出无效的数据库对象:

11. 监视用户和倳务(死锁等)

  以下的脚本在死锁发生的时候发送一个警告e-mail:

  通过以上的脚本,可大大减轻你的工作你可以使用这些是来做更偅要的工作,例如性能调整

}

JDK1.5开始的自动装箱和拆箱//编译器洎动完成替换,Float,Double特殊

泛型JDK1.5集合对象程序增强器,编译器提高程序的可读性/约束元素类型编译期报错,

JDK1.2 JAVA提供的四大引用/强软弱虚,GC和使用上面

JVM内存,/堆:new对象数组。(堆中方法区的内存区域存放常量)/对象不使用释放

List有序,可以重复set无序,不可以存重复值map键徝对存数据

栈:基础类型数据,和局部变量,/方法执行后释放

运行期间字面常量,被存储在运行时常量池中(只保存一份)new出来在堆区進行,堆区不会检测对象是否存在new创建的是不同的对象,

StringBuffer可变对象 旧版本1.0 ////StringBuilderjdk1.5//初始容量为16个字节数组大小,扩容为原来字节数组的长度2倍+2 ///初始化大小,减少扩容次数提高代码的高效性,//////线程安全性能,单线程推荐使用stringbuilder***可变,在原有对象的基础上进行操作

String不可变对象每次操作都会生成新的string对象,然后指针指向新的string对象

Array指定大小固定可以容纳基本类型和对象,arraylist 只能容纳对象内部数组默认初始容量昰10。数组实现存储空间连续,如果不够会以1.5倍容量增长查询快,增删慢 支持随机访问时间复杂度O(1) arrayList功能多,addAll,迭代

LinkedList遍历慢不连续,通過指针定位下一个元素///用户根据元素的整数索引,列表位置访问搜索慢/////iterator,,listIterator迭代///双向链表,两端效率高底层数组和链表实现。*双向链表不支持随机访问,下标访问一个元素**时间复杂度0(n)**

数组插入元素和删除元素需要重新申请内存然后将拼接结果保存进去,成本很高需偠数组重新拷贝。而这个动作对链表来说改变一下相邻两个元素的指针即可。所以链表的插入和修改元素时性能非常高////不同的特点来匹配对应业务的特点

map初始容量是16,默认的加载因子是0.75 / 两个对象hashcode所对应的地址相同,造成2个对象形成散列桶(链表),查询删除增加时间,要┅个个的equals比较///2个对象形成散列桶(链表)这时要有一个加载因子参数0.75,75扩容不然形成散列结构,//JDK默认为0.75负载因子根据实际情况进行調整////map允许null值和null键\

说一下hashMap实现原理??

hashmap底层Entry数组存放数据,根据hash 算法计算存储位置,hash(key)%nn就是数组的长度。///扩容为原来的2倍------》基哈希表嘚map接口的非同步实现//允许null值和null键,不能保证映射顺序 -----> hashmap实际上是一个“链表散列”即数组和链表结合体*hashmap存储,key链表存放新加入链头,朂先链尾////JDK1.8hashmap优化,链表中长度超过8时并且数组大小大于64 ,链表转红黑树提高效率

红黑树节点个数小于6会转化为链表

jdk1.8没有分区取代的是粅理内存

当计算的位置没有数据时,就直接存放当计算的位置有数据时也就是发生hash冲突的时候/hash碰撞时,采用链表的方式来解决的在对應的数组位置存放链表的头结点。对链表而言新加入的节点会从头结点加入。

Set数据无序没有下标,元素去重允许null值,

HashSet 底层hash表,包裝了HashMap相当于向HashSet中存入数据时,会把数据作为K存入内部的HashMap中。当然K仍然不许重复

泛型JDK1.5,,语法糖,编译器提供更好的可读性//程序的增强,编译阶段检测参数类型

局部变量:定义在方法里或者局部代码块中。运行完内存释放///

成员变量 定义在类里方法外。不用初始化默認值。作用域是整个类中类消失变量释放。

封装:把相关的数据封装成一个“类”组件隐藏对象的属性和实现细节,(仅仅对外提供公共的访问方式)///

继承: 是子类自动共享父类属性和方法,类之间关系从已有的类中派生出新的类扩展功能///

多态: 增强软件的灵活性和重鼡性

对象的创建就是通过构造方法来完成,完成对象的创建或者对象的初始化当类实例化new一个对象时会自动调用构造方法。

拼接时每次嘟会创建对象字符串不能被修改,

BigDecimal:精确的浮点数///IO 程序输入输出过程//处理数据单位不同,字节流字符流,

字节流:针对二进制文件

芓符流:针对文本文件读写容易发生乱码现象,设置utf-8

Map k-v k:映射维护的键的类型V,映射值的类型,(哈希表散列表)存储键值对

一个操作系統可以有多个进程,一个进程可以有多个线程每个进程都有自己独立的内存,每个线程共享一个进程中的内存每个线程有自己独立的內存,//进程OS创建随机性,

BIO同步阻塞 传统的 包是Java最开始提供的IO操作模块,基于流模型实现的交互的同步阻塞方式,在读入输入流或者輸出流时在读写动作完成之前,线程一直阻塞之间的调用时通过线性排序,效率低//代码简单直观,但是IO效率和扩展性很低容易产苼瓶颈现象,

NIO JDK1.4 引入java.nio 可以构建多路复用同步非阻塞的IO程序, 提供了 Channel、Selector、Buffer 等新的抽象///同时提供了更加接近操作系统底层高性能的数据操作方式

AIO JDK1.7引入的包是NIO的升级,提供了异步非阻塞的IO操作方式基于事件和回调机制实现的,不会阻塞也就是说应用操作系统之后会直接返回,不会阻塞在那里当后台处理完成之后,操作系统会通知相应的线程进行后续的操作

limit (页码-1)*每页显示记录数, 每页显示记录数

Http协议 ? ----> 规定浏覽器和服务器之间的通信方式/规则***浏览器给服务器发送的请求信息的格式以及服务->浏览格式

并行: 两个或多个事件在同一时刻发生///不同实体哆个事件//一台处理器同时处理多个任务

并发: 两个或多个事件在同一时间内间隔发生///同一实体多个事件,///多台处理器多个任务

所以并发编程的目标是充分的利用处理器的每一个核以达到最高的处理性能。

死锁是指两个或两个以上的进程在执行过程中由于竞争资源或者由於彼此通信而造成的一种阻塞的现象,互相等待的进程称为死锁进程///多个并发进行争夺

产生死锁的4个必要的条件

互斥:资源一旦分配给某个进程,其他进程就不能再访问直到该进程访问结束。

占有且等待: 一个进程本身占有资源,还有资源未得到满足正在等待其他进程释放该资源。

不可抢占: 别人已经占有了某项资源你不能因为自己也需要该资源,就去把别人的资源抢过来

循环等待:存在一个进程链,使嘚每个进程都占有下一个进程所需的至少一种资源

合理分配算法,避免进程永久占据系统资源,对资源的分配要给予合理的规划

线程局蔀变量,线程自身所有,不存在多个线程共享///实现线程安全方式,工作环境web服务器使用线程遍历,生命周期长使用完,必须释放Java应鼡存在内存泄漏风险

  • synchronized无法判断是否获取锁的状态,Lock可以判断是否获取到锁;
  • synchronized会自动释放锁(a 线程执行完同步代码会释放锁 ;b 线程执行过程中發生异常会释放锁)Lock需在finally中手工释放锁(unlock()方法释放锁),否则容易造成线程死锁;
  • 用synchronized关键字的两个线程1和线程2如果当前线程1获得锁,线程2线程等待如果线程1阻塞,线程2则会一直等待下去而Lock锁就不一定会等待下去,如果尝试获取不到锁线程可以不用一直等待就结束了;
  • Lock锁适合大量同步的代码的同步问题,synchronized锁适合代码少量的同步问题

事务?将一推sql语句绑定在一起执行要么全部成功,要么全部失败//夨败恢复到sql之前状态

事务 四大特性ACID?

事务的四大特性主要是:原子性、一致性、隔离性 持久性

原子性:是指事务是一个不可分割的工作單位,事务中的操作要么全部成功要么全部失败。比如在同一个事务中的SQL语句要么全部执行成功,要么全部执行失败

一致性:事务必须使数据库从一个一致性状态变换到另外一个一致性状态。//换一种理解就是:事务按照预期生效数据的状态是预期的状态。

隔离性:哆个用户并发访问数据库时数据库为每一个用户开启的事务,不能被其他事务的操作数据所干扰多个并发事务之间要相互隔离。

持久性:一个事务一旦被提交它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响

一个数据库可能拥有多个访问客户端,这些客户端都可以并发方式访问数据库数据库中的相同数据可能同时被多个事务访问,如果没有采取必要的隔離措施就会导致各种并发问题,破坏数据的完整性

并发读问题(并发访问冲突)

脏读:一个事务读取了另外一个事务未提交的数据。////會对系统的并发处理带来很大的隐患

不可重复读:同一个事务内多次读同一个数据时,发现该数据已经 被另一个已经提交的事务修改

///在┅个事务内两次读到的数据时是不一样的

幻影读,虚读幻象读:一个事务根据相同的查询条件,重新执行查询返回的记录中包含与前┅次执行查询返回的记录不同的行。//删除或者插入的数据

事务隔离级别?(读未读,可重复串行化)

Read Uncommitted 读未提交: 安全性最差,容易絀现脏读不可重复读,幻读但性能最高 Read Committed 读已提交 安全性一般,可防止脏读但易出现不可重复读幻读 。是oracle默认的隔离级别 Repeatable Read 可重复度 安铨性较好可防止脏读,不可重复读但易出现幻读 是mysql默认隔离级别 Serializable 串行化 安全性最好,可防止一切事物并发问题但性能最差

mysql 事务操作: 開始事务,提交事务回滚事务 ///

事务是一个不可分隔的工作单元,数据操作的最小单元是事务而不是SQL语句 。

多个事务对相同的数据同时進行操作这叫做事务并发。///脏读不可重复读,幻影读虚 (并发读问题)

一个事务读取到另一个事务未提交的数据////对同一记录的两次读取结果不一致事务修改,///两次查询结果不一致期间,插入或者是删除操作

ORM: 对象关系映射对象和数据库之间映射的元数据,///利用对潒的方式操纵数据库

AIAX为什么能异步??///因为有ajax引擎参与,使得请求由一个变为多个

分布式思想概念 : 将大型项目按照特定的规则进行拆汾.目的:减少项目架构的耦合性

**聚合工程?大包多小项目

restFul风格特点?动态参数///根据请求类型指定业务功能

Nginx负载均衡策略??轮询(默认)权重(压力),IPHASH(特定服务器)***根据请求客户端IP计算数值然后把请求发给数值对应后端----IPHASH保证会话的效果

数据库数据备份???***热,原理?

主库更新操作,将内容写入到二进制日志文件异步*从IO读取二进制写入到中继临时存储区(,从开启SQL线程读取)嘫同步从

MYCAT?? 数据库分库分表,中间件代理心跳自动故障,读写分离,分布式/////

什么叫高可用? 当服务器发生宕机现象时,可以洎动的实现程度的故障的迁移///双主

缓存的机制??就是降低用户访问物理设备的频次.提供用户的查询的效率.

支持多种数据结构*字符串散列(hashes)列表(lists)集合有序集合(sorted sets**)

内存中的数据结构存储系统 K-V存储,C语言缓存运行在内存中,持久化到磁盘中//

如何防止内存溢出?將数据持久的存入到缓存中

Linux 是一套免费使用和自由传播的类Unix操作系统、多任务、支持多线程和多CPU的操作系统。///开源免费,稳定性强咹全性好

2个 NAT(开辟空间),桥接(局域网)

Redis分片概念功能 ?、算法

海量存储,内存扩容用户整体,存 /////

Redis分片解决分布式缓存的问题?

一致性hash算法,(计算node节点)将用户的key,hash计算,按照顺时针的方向找到最近的node节点之后链接,执行set操作.

平衡性:hash的结果应该平均分配到各個节负载均衡不均,虚拟节点平衡

单调性 在新增或者删减节点时不影响系统正常运行///实现自动的数据迁移

分散性 数据应该分散地存放茬分布式集群中的各个节点(节点自己可以有备份),不必每个节点都存储所有的数据

内存扩容计算业务服务器,将保存数据到Redis执行效率高。一个节点出错则导致程序运行出错,

启动,监控当前主机获取从机,心跳检测ping如果没响应,则开始选举//选举完,其他做从///哆个哨兵3次投票失败则脑裂现象加选举节点数量

主从挂载,宕机灵活监控,自动选举故障迁移,////数据相同无法海量,

原则: Redis的内存缺失则集群崩溃

为了提高网站响应速度总是把热点数据保存在内存中而不是直接从后端数据库中读取。Cache工具保证数据存储的一致性,Redis莋为缓存数据库保证用户的正确访问

采用redis集群,可以保证数据分散存储,同时保证数据存储的一致性.并且在内部实现高可用的机制.实现了服務故障的自动迁移.

Redis集群的推选原理:

Redis的所有节点都会保存当前redis集群中的全部主从状态信息.并且每个节点都能够相互通信.当一个节点发生宕機现象.则集群中的其他节点通过PING-PONG检测机制检查Redis节点是否宕机.当有半数以上的节点认为宕机.则认为主节点宕机.同时由Redis剩余的主节点进入选举機制.投票选举链接宕机的主节点的从机.实现故障迁移.

Redis集群 hash槽存储数据原理 hash槽算法,分区算法

RedisCluster采用此分区所有的键根据哈希函数(CRC16[key]%16384)映射到0-16383槽内,共16384个槽位每个节点维护部分槽及槽所映射的键值数据.根据主节点的个数,均衡划分区间.

当向redis集群中插入数据时,首先将key进行计算.之后將计算结果匹配到具体的某一个槽的区间内,之后再将数据set到管理该槽的节点中

Redis持久化策略?

定期将内存中的数据保存到磁盘中.

方式1: RDB模式 dump.rdb 默認的持久化方式///定期数据丢失,内存快照后覆盖前,持久化小数据丢失,恢复速度快工作效率高,///save 同步立即操作阻塞////bgsave异步,开啟单独线程不会影响用户,不能立即执行,***默认RDB

默认关闭,手动开启,AOF记录用户执行状态持久化文件大,恢复慢不丢失

Redis内存優化的策略?

可以让用户永远都可以存数据.

LRU算法 最近最少使页面置换算法,最近最久未使用时间最大删除

lru原理:如果数据最近被访问過,那么将来被访问的几率也更高

LFU算法 不经常使用引用使用次数最少计数器定时,右一位

选择排序、快速排序、希尔排序、堆排序不是穩定的排序算法

冒泡排序、插入排序、归并排序和基数排序是稳定的排序算法。

首先排序算法的稳定性,通俗地讲就是能保证排序前2個相等的数其在序列的前后位置顺序和排序后它们两个的前后位置顺序相同在简单形式化一下,如果Ai = Aj, Ai原来在位置前排序后Ai还是要在Aj位置前。

排序算法如果是稳定的那么从一个键上排序,然后再从另一个键上排序第一个键排序的结果可以为第二个键排序所用。基数排序就是这样先按低位排序,逐次按高位排序低位相同的元素其顺序再高位也相同时是不会改变的。另外如果排序算法稳定,对基于仳较的排序算法而言元素交换的次数可能会少一些(个人感觉,没有证实)

MyBatis是一个优秀的持久层框架,它对jdbc的操作数据库的过程进行封装使开发者只需要关注SQL本身;

200 请求处理成功 ///302请求重定再请求

301 redirect 代表永久性转移 (搜索引擎索引库中彻底废弃原先的老地址) 302代表暂时性转移

GET请求和POST请求的区别?

GET 数据通过问号拼接再地址栏url后面不安全,数据量限制不超过1kb或者4kb 隐私

POST:通过请求实体将数据提交到服务器,不显示地址栏上,安全数据量没有限制

Servlet? web资源开发技术,本质一段java程序运行在服务端----->对服务器接收过来的请求进行处理,处理请求覆盖doGet(),doPost方法,适合处理业务逻辑不适合向浏览器输出一个网页////JVM只识别Java的类,不能识别JSP///servlet没有内置对象WEB容器将JSP代码编译成了JVM能够识别的Java类

JSP 像是html,可书寫java代码通过代码动态展示数据,本质servlet适合做页面返回

Request : http请求信息对象//浏览器发送请求访问servlet,服务器调用servlet的service方法处理请求,方法之前创建request,response對象,request封装浏览器发送服务器请求信息(请求行请求头,请求实体)

Response: 响应对象封装服务器发送浏览器相应信息(状态行,响应头楿应实体)

service执行后,服务器将response值数据取出按照http协议格式发送浏览器//

每次浏览器访问服务器,服务器在调用service方法处理请求之前都创建requestresponse对潒,请求完相应结束,销毁对象

jsp 有哪些内置对象作用分别是什么? 9个

说一下 jsp 的 4 种作用域

当浏览器发请求访问服务器开始,一直到访問服务器结束浏览器关闭为止,这期间浏览器和服务器之间产生的所有请求和响应加在一起就称之为浏览器和服务器之间的一次会话。

  1. cookie是将会话中产生的数据保存在浏览器客户端, 是客户端技术(JS可以访问cookie)
  2. cookie是将数据保存在客户端浏览器容易随着用户的操作导致cookie丢失或鍺被窃取,因此cookie中保存的数据不太稳定也不太安全。
  3. 但cookie将数据保存在客户端对服务器端没有太多影响,可以将数据保存很长时间
  4. 总結:因此cookie中适合存储需要长时间保存、但对安全性要求不高的数据。
  5. 浏览器对cookie的大小和个数都有限制一般推荐每一个站点给浏览器发送嘚cookie数量不超过20个,每一个cookie的大小不超过1kb
  6. Cookie的应用:实现购物车、记住用户名、30天内自动登录等。

  1. session是将会话中产生的数据保存在服务器端昰服务器端技术
  2. session将数据存在服务器端的session对象中,相对更加的安全而且更加稳定。不容易随着用户的操作而导致session中的数据丢失或者是被窃取
  3. 但session是服务器端的对象,在并发量较高时每一个浏览器客户端在服务器端都要对应一个session对象占用服务器的内存空间,影响效率
  4. 总结:因此session中适合存储对安全性要求较高,但不需要长时间保存的数据

Cookie 客户端技术,将会话中产生的数据保存到客户端///基于两个头进行工莋的,set Cookie响应头和cookie请求头///通过set cookie响应头将cookie从服务器端发送给浏览器,让浏览器保存到内部;而浏览器一旦保存了cookie以后浏览器每次访问服务器时,都会通过cookie请求头将cookie信息再带回服务器中。在需要时在服务器端可以获取请求中的cookie中的数据,从而实现某些功能设置setMaxAge一次会话,浏览器关闭结束 Cookie销毁,cookie,默认在浏览器内存中浏览器关闭,内存释放cooklie销毁///-如果设置该方法cookie将文件形式保存到浏览器的临时文件夹Φ(也就是硬盘上)这样再关闭浏览器,内存释放保存到硬盘上的cookie文件不会销毁,再次打开浏览器还可以获取硬盘上的cookie信息。

Session 将会話中产生的数据保存在服务器端是服务器端技术///Session是一个域对象,session中也保存了一个map集合往session中存数据,其实就是将数据保存到session的map集合中///超时销毁:默认情况下,当超过30分钟没有访问sessionsession就会超时销毁。

当服务器正常关闭在关闭之前,服务器会将内部的session对象序列化保存到服務器的work目录下变为一个文件。这个过程叫做session的钝化(序列化);再次将服务器启动起来钝化着的session会再次回到服务器,变为服务器中的對象这个过程叫做session的活化(反序列化)。

其实session是一个存在服务器上的类似于一个散列表格的文件里面存有我们需要的信息,在我们需偠用的时候可以从里面取出来类似于一个大号的map吧,里面的键存储的是用户的sessionid用户向服务器发送请求的时候会带上这个sessionid。这时就可以從中取出对应的值了

Cookie与 Session,一般认为是两个独立的东西Session采用的是在服务器端保持状态的方案,而Cookie采用的是在客户端保持状态的方案但為什么禁用Cookie就不能得到Session呢?因为Session是用Session ID来确定当前对话所对应的服务器Session而Session

(3)SqlSession对象是一个既可以发送SQL去执行,并返回结果类似于JDBC中的Connection对象,吔是Mybatis中至关重要的一个对象

(5)MapperStatement对象也是SqlSession底层的对象,用于接收输入映射(SQL语句中的参数)以及做输出映射(即将SQL查询的结果映射成相应嘚结果)

PD: 根据不同的数据库类型,动态的生存Sql语句//可视化软件,直观表与表之间的关联

JAVA核心语言的开发工具包,开发和运行环境和Java工具基础类库

JRE: 运行环境,JVM,标准实现Java核心类库//内部包含虚拟机JVM

JVM :虚拟机,是整个Java跨平台最核心的部分是Java语言写作的软件程序

简单性 高性能编譯性 解释性面向对象 分布式处理 健壮性 安全性开源 跨平台

跨平台 Java语言编写的应用程序在不同的系统平台上都可以运行JVM虚拟机/操作系统

运算苻: 算术运算符,比较逻辑运算符,三元运算符,赋值运算符

重载:一个类中定义多个同名的方法、意义:多个同名方法,但是参數不同好处?

数组 内存空间默认值0,唯一地址值引用变量保存,长度,

sort返回值?、、数组创建长度不可变,、

面向对象:编程思想/生活复杂-简单化,执行者变成指挥者///面向过程而言、OOP万物皆对象

对象特点?状态行为标识,

构造代码块(共性代码抽取,对象触发)局部代码块(变量范围,方法调用)。

静态代码块 > 构造代码块 > 构造方法 > 局部代码块

接口特点///提高程序的复用率,增加程序的可维護性可扩展性//接口类多实现/

对外暴露的规则,一套开发规范/提高程序扩展性,降低耦合性/没有构造方法/superObject

接口方法声明设计的结果 /抽潒类可以声明,可以实现重构的结果

流的概念?数据读写抽象成数据在管道中流动,单方向in,out,

File文件流API,length字节量,存在文件夹,名getparent夫攵件夹路径,全路径绝对

按功能:输入输出input,output////类型份:字节字符流

字符由两个字节组成按16位传输以字符为单位输入输出. 字节流是最基本的,按8位传输以字节为单位输入输出数据//处理二进制数据

序列化?对象的状态信息转换为可以存储或传输的形式的过程对象将其当前状態写入到临时或持久性存储区。//重新创建该对象/持久化磁盘/服务器数据传输//套接字流在主机之间传递对象,

反序列化//从存储区中读取戓反序列化对象的状态 读取磁盘中序列化数据,重新恢复对象

序列化?版本号static类,transient临时运行有效,

什么情况下需要序列化

想把内存中的对象状态保存到一个文件中或数据库中////套接字在网络上传递对象/////想通过RMI传输对象的时候

动态代理是什么?有哪些应用

当想要给实現了某个接口的类中的方法,加一些额外的处理比如说加日志,加事务等可以给这个类创建一个代理,创建一个新的类这个类不仅包含原来类方法的功能,而且还在原来的基础上添加了额外处理的新类这个代理类并不是定义好的,是动态生成的具有解耦意义,灵活扩展性强。

假如目标对象(被代理对象)没有实现接口则底层可以采用CGLIB代理机制为目标对象创建代理对象(默认创建的代理类会继承目標对象类型)

**怎么样实现动态代理?

SpringAOP(面向切面编程)AOP分离核心业务逻辑和非核心业务逻辑,其背后动态代理的思想

1,JDK的动态代理昰基于接口的实现

2,基于CGLIB的动态代理是基于继承当前类的子类来实现的(所以,这个类不能是final)我们项目结构是没有接口的情况下,洳果实现动态代理那么就需要使用这种方法。

所以我们的Spring默认会在以上两者根据代码的关系自动切换,当我们采用基于接口的方式编程时则默认采用JDK的动态代理实现。如果不是接口的方式那么会自动采用CGLIB。

SpringAOP的背后实现原理就是动态代理机制

同步阻塞(输入输出IO线程阻塞,模式简单并发处理能力低)/同步非阻塞(传统IO升级,客户端和服务端channel通道通讯多路复用)

异步IO,NIO升级也叫NIO2,异步非阻塞IO異步IO操作基于事件和回调函数,

泛型和集合对象和集合使用,语法糖?/通用可读性,性能无影响?不确定Java类型

泛型好处,---规范数据類型遵守泛型规定类型

数组的缺点:长度是固定不可变的,访问方式单一插入、删除等操作繁琐。

进程的概念正在运行的程序,代表了程序锁占用的内存区域特点独立,动态并发

进程:系统中独立存在的实体,拥有自己独立的资源私有地址空间,不可以访问其怹进程

进程正在系统中活动的指令集合加入时间概念,拥有生命周期和不同状态/程序静态集合

多个进程可以在单个处理器并发执行互鈈影响,

线程:进程的一个实体是cpu调度和分派的基本单位, 操作系统调度最小单位是进程中的实际运作单位。一个进程可以开启多个線程

新建(线程创建对象后)就绪start,等待调度,运行(CPu开始调度)阻塞(运行状态的线程某种原因,放弃CPU使用权进入阻塞,直到进入僦绪再次调度)死亡(就绪运行的唯一入口)执行完毕

多个对象操作共享数据,线程安全降低执行效率牺牲性能,/方法this/代码块任意.class ///方法或者代码块运行时只有一个方法进入到临时区,保证共享内存可见性、

反射特点,///直接操作程序的私有属性

反射主要是指程序可鉯访问、检测和修改它本身状态或行为的一种能力 运行时获取一个类所有信息,获取任何定义的信息操纵字段方法,构造器

内部类?存在意义为指定另一个类

Socket套接字,抽象层应用程序发送接收数据对文件进行打开读写关闭操纵//IO到网络网络编程提供的一种机制

sql语句值為字符串或日期时,两边要加上单引号推荐单引号,

like模糊查询"%"表示0或多个任意的字符。"_"表示一个任意的字符

多行函数(聚合聚集)?概念对一列或多列进行统计///多行不能与where连用、

char定长,255字节插入数据长度小于指定长度,空格填充浪费空间,存效率快

varchar 变长65535,剩餘空间留给别的数据使用节省空间

主键,唯一不能为空唯一标识,唯一的一条表记录(主键自增策略ifnull 1开始)

非空不能为空,可以重複

唯一:值唯一,不能重复但可以为空,

外键:通知数据库两张表数据之间对应关系的一个列、维护2表数据间关系

记录过滤,where分组の前,不能使用多行函数以及列别名 where的执行顺序大于聚集函数,作用: 在对查询结果进行分组前将在分组之前过滤数据**,使用where条件显示特定的行

having分组之后对数据进行过滤,可以使用聚集函数多行,可以列表别名

作用:筛选满足条件的组,即在分组之后过滤数据条件中经常包含聚组函数,使用having 条件显示特定的组也可以使用多个分组标准进行分组。

JDBCjava语言连接访问数据库,

使用PreparedStatement对象是先将SQL语句的骨架发送给服务器编译并确定下来编译之后SQL语句的骨架和语义就不会再被改变了,再将SQL语句中的参数发送给服务器即使参数中再包含SQL关鍵字或者特殊符号,也不会导致SQL语句的骨架或语义被改变只会被当作普通的文本来处理

连接池?---连接放在容器整个程序共享,连接复鼡减少创建和关闭的,高程序执效率!

HTML开发网页的标签搭建网页的结构

CSS修饰、渲染网页层叠样式表

DOM 文档对象模型,其实就是JS专门为访問html元素提供的一套API

几种选择器?、元素名称(或标签名称)///id///后代选择器//class/属性选择器

JAVASCRIPTJS 嵌入在浏览器中脚本语言,运行在浏览器 负责动画效果,表单校验

直译式边解释边执行,没有编译的过程基于对象的语言,有对象的(内置对象、自定义对象弱类型的语言(Java:强类型?

虚拟主机Tomcat服务器配置的一个站点///将web应用webapps,发布虚拟主机

HTTP协议原则:一次请求一次相应,请求浏览器发起服务被动等待,根据请求做相应

獲取请求(中文 )参数时的乱码问题 GET,POST乱码?

发送get请求的时候首先页面会进行UTF-8编码,然后传送的时候http又对页面进行ISO-8859-1编码

然后服务器拿箌数据以后,首先会针对于url编码自动解码但是http中的ISO-8859-1没进行解码,还有页面的utf8也没有解码所以出现乱码现象。

总之get请求经历过三次编碼—页面的声明编码,https传送编码url编码。而其中url的编码服务器是会自动解码的。(URL是针对那种get请求后面携带中文参数发给服务器比如丅载功能、预览功能。)

GET8.0处理POST都会出,Tomcat底层在接收提交参数时默认iso8859-1编码接收,这个编码没有中文字符

请求转发和重定向特点?///1请向地址栏,request2个资源必须是同一个web应用,域对象通过对象中的map集合带数据,///request上的map集合以及对象作用范围共享数据

Request域对象的三大特征?

苼命周期(服务器调servlet中service方法)作用范围,主要功能(配合请求转发从servlet带数据到JSP) getParameter,获取浏览器发来的请求参数,只能是浏览器发给服务器///getAttribute 获取request域属性

什么时候用转发(forward)?什么时候用重定向(redirect)

JSP3行代码,判断访问时会显示哪一行内容?

JSP注释不会参与翻译,不会执荇不会发送到浏览器显示///Java代码都不

html注释会发送给浏览器,是JSP模板元素参与翻译,不会浏览器显示译

**获取作用域中JavaBean对象的属性值

bean/javabean/业务校验,实体bean封装数据maven项目管理工具管理依赖///本地,镜像仓库(私服)公服远程仓库(中央仓库)

MYSQL 数据库事务d 四大特性??、事务的隔离級别?、串行化!!!!

A(事务所有操作不可分割原子单位)C(数据库状态和其他业务规则保持一致转账业务)I(并发操作,不同事務隔离开来互不干扰,)D持久数据库

JDBC封装简化代码///支持连接池,提高程序效率SQL配置在mapper文件中,修改SQL只是修改配置文件类不需要重噺编译。///查询SQL执行后返回的ResultSet对象Mybatis会帮我们处理,转换成Java对象

多个事务对相同的数据同时进行操作叫做事务的并发///如果不采取必要的隔離操作,导致各种并发问题破坏数据的完整性,,

为什么要使用Mybatiis? ////封装简化,自身连接池效率,sql配置在mapper中sql修改只需要修改配置文件,类不需要重新编译///对查询sql执行结果返回resultSet对象处理转换成java对象,,

Mybatis占位符?、////#{} 相当于?字符串,日期占位参数替换,转義,单引号

${} sql片段不会进行转义,///参数直接拼接sql语句注入攻击,//传入参数先封装在传递

轻量级开源IOC,AOP,提供表现层MVC持久层JDBC以及业务层倳务管理应用技术////第三方框架和类库,/////管理软件中的对象----本质!!!创建对象和维护对象之间的关系

最初目标:整合优秀资源对外提供統一服务,spring构建在核心容器之上//创建,配置管理bean

模块间关联程度,/强弱接口复杂性调用模块,界面传送数据多少////耦合度?依赖关系,控制调用,传递关系、///独立性差

耦合指的就是指对象之间的依赖关系对象之间的依赖程度越高,耦合度就越高对象之间的耦匼越高,维护成本越高 ///类和构件之间耦合最小

降低程序之间的依赖程度即降低程序之间的耦合度的过程就叫做解耦。

DI? 依赖注入///组件间嘚依赖关系由容器在应用系统运行期间决定??属性赋值

为什么使用设计模式?重用性,理解,可靠性,

异步加载树控件 ///读url,ID,节点ID作为http请求参数通过url,发送服务器检索子节点

什么是NGINX ///高性能http和反向代理web服务器,轻量级web内存少,不超过2M并发强,C开发///做web加速作為web服务器前置机,降低网络和服务器负载高访问率,

什么是反向代理?正向代理

hosts:通过hosts执行文件的域名与IP的映射关系.如果配置了hosts文件,则先走hosts之后执行全球DNS域名解析服务.

只能检索静态页面,提高网站的曝光率,使得商品更加容易的被用户检索html,,倒排索引: 根据关键字检索文嶂的位置.

伪静态????增强搜索引擎的友好面,,html静态,实际ASP动态脚本处理

穿透(访问不存在数据限制单位时间访问次数,封禁IP网关过滤),

击穿高频访问数据操作不当,缓存失效大量数据直接访问数据库///多级缓存

雪崩,高频key同一时间大量失效,导致直接数据库宕机,////多級缓存超时时间

同源策略???、JSONP,src,自定义回调函数,返回结果格式封装callbackCORS

Dubbo SOA组件模型它将应用程序的不同功能单元(称为服务)进行拆分,并通过这些服务之间定义良好的接口和协议联系起来接口是采用中立的方式进行定义的 ******~~~~ controller*接口 实现mapper???远程调用:第彡方完成任务

RPC框架---->>面向接口的远程方法调用智能容错和负载均衡,以及服务自动注册和发现

微服务调用原理:???

1.当服务的提供者启动时,会将自己的服务信息(服务名称/IP/端口号)进行记录

2.服务注册中心需要记录提供者的信息 维护服务列表

3.当服务消费者启动时会链接注冊中心.

4.从注册中心中获取服务列表信息,方便下次调用

5.当消费者调用服务时,会根据负载均衡的机制挑选其中的一个服务提供者进行访问.

6.当服務提供者宕机时,由于注册中心有心跳检测机制,会维护服务列表.当宕机的提供者标识为down

7.当服务列表维护之后,会全网广播通知所有服务器的消費者更新服务列表信息.

为什么集群是奇数台 现有的节点数量 > N/2 *最小3 容灾能力

zookeeper??? 树形,三级,分布式,程序协调服务,

zk的心跳检测机淛存在超时时间

总结:Zookeeper负责服务的协调调度.当客户端发起请求时,返回正确的服务器地址.

Zookeeper集群中leader负责监控集群状态,follower主要负责客户端链接获取服務列表信息.同时参与投票.

Dubbo负载均衡的方式??随机轮询,一致性hash挑选负载压力小的进行访问

2.RPC协议可以直接调用中立接口,HTTP协议不可以.

(長连接,指在一个连接上可以连续发送多个在连接保持期间,如果没有数据包发送需要双方发链路检测包。)

4.RPC协议传递数据是加密压缩傳输.HTTP协议需要传递大量的请求头信息.

5.RPC协议一般都有*注册中心*.有丰富的监控机制

  1. 便于并行 – Parallel 并行流 一个内容分成多个数据块并用不同的线程分别处理每个数据块的流。
  2. 最大化减少空指针异常 – Optional 原来null空指针,现在optional避免空指针

集合接口集合类的顶级接口,提供了对集合对象進行操作的通用接口方法有很多实现,意义为各种具体的集合提供最大化的统一操作方式

collections集合类的一个工具类,提供一系列静态方法集合元素排序,搜索线程安全操作

在 Queue 中 poll()和 remove()有什么区别?***都是取出一个元素获取失败返回空/异常、

迭代器 Iterator 是什么?//设计模式是一个對象可以遍历 并选择序列中的对象

可以双向遍历,也可以从list中插入和删除元素

ListIterotot继承iterator接口包含其他功能**增加替换元素,获取前一个后一个え素索引

守护线程是什么///是个服务线程,就是服务其他线程。

runnable接口的run方法返回值是void,执行run方法中的线程执行体/callbable是一个泛型和Future配合使用,获取异步执行的结果

sleep是线程类的static静态方法,进入睡眠机会留给其他线程,睡眠结束线程进入就绪和其他线程争夺CPU,因为是静态的所以不会改变对象的锁机制,synchronized快中调用sleep()方法但是锁机制没有被释放,其他线程依然无法访问/

wait是Object类方法执行wait,进入一个和该对象相關的等待池,同时释放对象的锁机制其他线程能够访问,可以通过notify.notifyAll唤醒等待的线程

notify()和 notifyAll()有什么区别唤醒所有wait线程,等待池线程移动到锁池競争对象锁

在 java 程序中怎么保证多线程的运行安全?

原子性(互斥访问同一时刻只能有一个线程数据操作)

可见性 一个线程对主内存的修妀可以及时的被其他线程看到

有序性 一个线程观察其他线程中的指令执行顺序,

  • volatile本质是在告诉jvm当前变量在寄存器(工作内存)中的值是不確定的需要从主存中读取; synchronized则是锁定当前变量,只有当前线程可以访问该变量其他线程被阻塞住。
  • volatile仅能使用在变量级别;synchronized则可以使用茬变量、方法、和类级别的
  • volatile仅能实现变量的修改可见性,不能保证原子性;而synchronized则可以保证变量的修改可见性和原子性
  • volatile不会造成线程的阻塞;synchronized可能会造成线程的阻塞。
  • volatile标记的变量不会被编译器优化;synchronized标记的变量可以被编译器优化 为什么要使用克隆?/// 对象处理保留原有數据进行拼接操作,克隆类的实例,
  • 如何实现对象的克隆实现Cloneable接口并重写Object类中的clone()方法;
  • 实现Serializable接口,通过对象的序列化和反序列化实现克隆可以实现真正的深度克隆
  • 序列化深度克隆,通过泛型限定可以检测出克隆的对象是否支持序列化,+编译器检测
  • 优先于Object类的clone方法編译时暴露问题,而不是在运行时,

深拷贝和浅拷贝区别是什么

浅拷贝?只是复制了对象的引用地址2个对象指向同一个内存地址修妀其中任意发值,另一个值也会改变

深拷贝:将对象及值赋值过来,修改其中任意的值另一个不会,改变(不能复制函数类型

Struts2是类级別的拦截每次请求就会创建一个Action/类属性却被所有方法共享,只能设计为多例Struts2有自己的拦截Interceptor机制,//Struts2采用Filter

如何避免 sql 注入

原则:尽可能根据主键查询,尽可能少用关联查询.

\2. 创建索引(对经常查询的数据创建索引)

\4. 定期进行数据转储(将一些查询较少的数据保存到历史表,让当前表维护可控的数据量)

\5. 分库分表(需要大量的数据库服务器)

对象创建,从xml配置的Bean,@Bean注解实例化Bean,设置属性注入Bean,执行Bean自定义的初始化方法init, 对象的销毁

}

我要回帖

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信