学雷锋 做美德少年网站,内蒙古建设厅网站官网,敬请期待英文怎么写,网站三大标签1.基本概念 java代码执行 代码编译class#xff1a;javac 源文件通过编译器产生字节码文件#xff0c;字节码文件通过jvm的解释器编译成机器上的机器码 装载class#xff1a;ClassLoader执行class#xff1a; 解释执行编译执行 client compilerserver compiler 内存管理…1.基本概念 java代码执行 代码编译classjavac 源文件通过编译器产生字节码文件字节码文件通过jvm的解释器编译成机器上的机器码 装载classClassLoader执行class 解释执行编译执行 client compilerserver compiler 内存管理 内存空间 方法区堆方法栈本地方法栈pc寄存器 内存分配 堆上分配TLAB分配栈上分配 内存回收 算法 copymark-sweepmark-compact sun jdk 分代回收 新生代可用的GC 串行copying并行回收copying并行copying Minor GC触发机制及日志格式旧生代可用的GC 串行Mark -Sweep-compact并行compacting并发mark-sweep Full GC触发机制以及日志格式 GC参数G1 内存状况分析 jconsolevisuaivmjstatjmapmat 线程资源同步和交互机制 线程资源同步 线程资源执行机制线程资源同步机制 Synchronized实现机制lock/unlock的实现机制 线程交互机制 Object.wait/notify/notifyAll-Double check pattem并发包提供的交互机制 semaphorecountdownLatch 线程状态及分析方法 jstacktda 2.jvm内存 线程私有 程序计数器 指向虚拟机字节码指令的位置唯一一个无oom的区域 虚拟机栈 虚拟机栈和线程的生命周期相同一个线程中每调用一个方法创建一个栈帧栈帧 本地变量表操作数栈对运行时常量池的引用 异常 线程请求的栈深度大于jvm所允许的深度StackOverflowError若JVM允许动态扩展若无法申请到足够内存OutOfMemoryError 本地方法栈 异常 线程请求的栈深度大于jvm所允许的深度StackOverflowError若jvm允许动态扩展若无法申请到足够内存OutOfmemeryError 线程共享 方法区永久代 运行时常量池 类实例区java堆-运行时数据区 创建的对象和数组都保存在java堆内存中也是垃圾收集器进行垃圾收集的最重要的内存区域。新生代edenfrom surviorto survivor老生代异常 直接内存 不受jvm gc管理
3.jvm运行时内存
新生代占据堆的1/3空间会频繁触发MinorGC进行垃圾回收 Eden区java对象出生地当Eden区不够会触发minorGC对新生代进行一次垃圾回收ServivorFrom上一次GC的幸存者作为这一次GC的被扫描者ServivorTo保留了一次MinorGC过程中的幸存者MinorGC的过程复制-清空-互换 eden、servivorFrom复制到ServivorTo年龄1清空edenservivorFromservivorTo和servivorFrom互换 老年代 存放应用程序中生命周期长的内存对象 。当无法找到足够大的连续空间分配给新创建的较大对象时也会提前触发一次 MajorGC 进行垃圾回收腾出空间。MajorGC标记清除算法扫描一次所有老年代标记存活对象回收没有标记的对象会产生内存碎片为减少内存损耗一般需要进行合成或者标记出来方便下次直接分配当老年代也装不下了抛出oom异常。 永久代内存的永久保存区域存放Class和Meta元数据的信息。Class被加载时被放到永久区域和存放实例区域不同GC不会在主程序运行期对永久区域进行清理导致永久代区域会随着加载class的增多而胀满最终抛出oom异常 java8与元数据元空间不在虚拟机中而是使用本地内存元空间大小受本地内存的限制类的元数据放入native memory 字符串池和类的静态变量放入java堆中这样可以加载多少类的元数据就不再由maxpermSize控制而由实际可用空间来控制。
4.垃圾回收算法
总结 gc 哪些内存需要回收什么时候回收怎么回收 哪些对象死亡 引用计数法循环引用的问题根搜索算法 GC roots需要经过两次标记两次标记后仍然是可回收对象则将面临回收Gc roots可达性分析 VM栈中的引用方法区中的静态引用JNI中的引用 垃圾收集算法 标记清除 标记需回收对象回收标记的效率低内存碎片多 复制可用内存被压缩到了原本的一半标记整理标记后存活对象移向内存一端清除端边界外的对象分代收集根据对象存活的不同生命周期将内存分为不同的域。 新生代与复制算法edenfrom spaceto space 81:1老年代和标记复制算法每次只回收少量对象 垃圾收集器 serialparNewparallel scavengeserial oldparallel oldcms 参数 XmsXmxXmn-XX。。。
5.java四种引用类型
强引用把一个对象赋给一个引用变量这个引用变量就是一个强引用。当一个对象被强引用变量引用时它处于可达状态它是不可能被垃圾回收机制回收的软引用需要用 SoftReference 类来实现对于只有软引用的对象来说当系统内存足够时它不会被回收当系统内存空间不足时它会被回收。软引用通常用在对内存敏感的程序中弱引用需要用weakReference类实现比软引用的生存期更短对于只有弱引用的对象来说只要垃圾回收机制一运行不管jvm的内存空间是否足够总会回收该对象占用的内存虚引用需要用phantomRerence类来实现不能单独使用必须和引用队列联合使用主要作用是跟踪对象被垃圾回收的状态。
6.垃圾收集器
Serial垃圾收集器单线程复制算法ParNew垃圾回收器多线程复制算法默认开启的cpu数目相同的线程数Parallel scavenge收集器多线程复制算法高效。适用于在后台运算二不需要太多交互的任务。Serial old收集器单线程标记整理算法 与新生代的parallel scavenge收集器搭配使用作为老年代中使用cms收集器的后备垃圾收集方案 parallel old收集器多线程标记整理算法cms收集器多线程标记清除算法主要目标是获取最短垃圾回收停顿时间多线程清除算法。 初始标记标记和gc roots 直接关联对象速度快。并发标记进行gc roots 跟踪过程和用户线程一起工作重新标记修正并发标记期间因用户程序继续运行而导致标记产生变动的那一部分对象的标记记录并发清除清除gc roots不可达对象和用户线程一起工作。 G1收集器 基于标记整理算法不产生内存碎片精确控制停顿时间不牺牲吞吐量前提下实现低停顿垃圾回收。内存划分为大小固定的几个独立区域并跟踪这些区域的垃圾收集进度每次根据所允许的收集时间优先回收垃圾最多的区域区域划分和优先级区域回收机制确保G1收集器可以在有限时间获得最高的垃圾收集效率
7.Java IO/NIO
阻塞IO模型读写数据过程中会发生阻塞现象。 用户线程发出 IO 请求之后内核会去查看数据是否就绪 没有就绪就会等待数据就绪而用户线程就会处于阻塞状态用户线程交出 CPU。当数据就绪之后内核会将数据拷贝到用户线程并返回结果给用户线程用户线程才解除 block 状态。典型的阻塞 IO 模型的例子为 data socket.read();如果数据没有就绪就会一直阻塞在 read 方法 非阻塞IO模型 当用户线程发起一个 read 操作不需要等待马上就得到了一个结果。 如果结果是一个error 时数据还没有准备好可以再次发送 read 操作。内核中的数据准备好了并又再次收到了用户线程的请求它马上就将数据拷贝到了用户线程然后返回。非阻塞 IO 模型中用户线程需要不断地询问内核数据是否就绪非阻塞 IO不会交出 CPU一直占用 CPU。问题 while 循环中需要不断地去询问内核数据是否就绪这样会导致 CPU 占用率非常高因此一般情况下很少使用 while 循环这种方式来读取数据。 多路复用IO模型 有一个线程不断去轮询多个 socket 的状态只有当 socket 真正有读写事件时才真正调用实际的 IO 读写操作。通过 selector.select()去查询每个通道是否有到达事件没有事件则一直阻塞在那里这种方式会导致用户线程的阻塞。只有当socket 真正有读写事件发生才会占用资源来进行实际的读写操作。多路复用 IO 比较适合连接数比较多的情况。注意多路复用 IO 模型是通过轮询的方式来检测是否有事件到达并且对到达的事件逐一进行响应。因此对于多路复用 IO 模型来说 一旦事件响应体很大那么就会导致后续的事件迟迟得不到处理并且会影响新的事件轮询 信号驱动IO模型 当用户线程发起一个 IO 请求操作会给对应的 socket 注册一个信号函 数用户线程会继续执行当内核数据就绪时会发送一个信号给用户线程用户线程接收到信号之后便在信号函数中调用 IO 读写操作来进行实际的 IO 请求操作 异步IO模型 用户线程发起 read 操作之后立刻就可以开始去做其它的事。从内核的角度当它受到一个 asynchronous read 之后会立刻返回说明 read 请求已经成功发起了因此不会对用户线程产生任何 block。内核会等待数据准备完成然后将数据拷贝到用户线程当这一切都完成之后内核会给用户线程发送一个信号告诉它 read 操作完成了。用户线程只需要先发起一个请求当接收内核返回的成功信号时表示 IO 操作已经完成可以直接去使用数据了。异步 IO 模型中 IO 操作的两个阶段都不会阻塞用户线程这两个阶段都是由内核自动完成然后发送一个信号告知用户线程操作已完成。用户线程中不需要再次调用 IO 函数进行具体的读写。 在信号驱动模型中当用户线程接收到信号表示数据已经就绪然后需要用户线程调用 IO 函数进行实际的读写操作在异步 IO 模型中收到信号表示 IO 操作已经完成不需要再在用户线程中调用 IO 函数进行实际的读写操作。 java NIO 三大核心部分 Channel(通道)、Buffer(缓冲区)、Selector数据总是从通道读取到缓冲区中或者从缓冲区写入到通道中。Selector(选择区)用于监听多个通道的事件比如连接打开数据到达。因此单个线程可以监听多个数据通道。NIO 和传统 IO 之间区别是 IO 是面向流的 NIO 是面向缓冲区的缓冲区 Java IO 面向流每次从流中读一个或多个字节直至读取所有字节它们没有被缓存在任何地方。此外它不能前后移动流中的数据。如果需要前后移动从流中读取的数据 需要先将它缓存到一个缓冲区。NIO 数据读取到一个稍后处理的缓冲区需要时可在缓冲区中前后移动。增加了处理过程中的灵活性。但是需检查是否该缓冲区中包含所有需要处理的数据。而且需确保当更多的数据读入缓冲区时不要覆盖缓冲区里尚未处理的数据 非阻塞 IO 的各种流是阻塞的。当一个线程调用 read() 或 write()时该线程被阻塞直到有一些数据被读取或数据完全写入。该线程在此期间不能再干任何事情了。NIO 的非阻塞模式使一个线程从某通道发送请求读取数据但仅能得到目前可用的数据如果目前没有数据可用时就什么都不会获取。而不是保持线程阻塞所以直至数据变的可以读取之前该线程可以继续做其他的事情。 非阻塞写也是如此。一个线程请求写入一些数据到某通道但不需要等待它完全写入这个线程同时可以去做别的事情。 线程通常将非阻塞 IO 的空闲时间用于在其它通道上 执行 IO 操作所以一个单独的线程现在可以管理多个输入和输出通道channel。 channelChannel 和 IO 中的 Stream(流)是差不多一个等级的。 Stream 是单向的 InputStream, OutputStreamChannel 是双向的既可以用来进行读操作又可以用来进行写操作。主要实现有FileChannel、DatagramChannel、SocketChannel、ServerSocketChannel分别可以对应文件 IO、 UDP 和 TCPServer 和 Client。 Buffer缓冲区一个连续数组channel提供从文件、网络读取数据的渠道但读取或写入的数据必须经由Buffer。 客户端发送数据先将数据存到Buffer然后将Buffer中的内容写入通道服务端接收数据通过Channel将数据读入到Buffer中然后再从Buffer中取出数据来处理。 Selector能够检测多个注册的通道上是否有事件发生如果有时间发生便获取事件然后针对每个事件进行相应的响应处理。一个单线程管理多个通道管理多个连接。
8.JVM类加载机制
类加载机制加载验证准备解析初始化加载会在内存中生成一个代表这个类的 java.lang.Class 对象 作为方法区这个类的各种数据的入口。可以从 ZIP 包中读取比如从 jar 包和 war 包中读取也可以在运行时计算生成动态代理也可以由其它文件生成比如将 JSP 文件转换成对应的 Class 类。验证主要目的是为了确保class文件的字节流中包含的信息是否符合当前虚拟机的要求并且不会危害虚拟机自身安全准备正式为类变量分配内存并设置类变量的初始值阶段即在方法区中分配这些变量所使用的内存空间解析虚拟机将常量池中的符号引用替换为直接引用的过程 符号引用引用的目标并不一定要已经加载到内存中。 各种虚拟机实现的内存布局可以各不相同直接引用指向目标的指针相对偏移量或是一个能间接定位到目标的句柄。如果有了直接引用那引用的目标必定已经在内存中存在 初始化类加载的最后阶段真正执行类中定义的java程序代码 执行类构造器方法的过程。
9.类加载器
启动类加载器(Bootstrap ClassLoader) 负责加载 JAVA_HOME\lib 目录中的 或通过-Xbootclasspath 参数指定路径中的 且被虚拟机认可按文件名识别 如 rt.jar 的类。扩展类加载器(Extension ClassLoader)负责加载 JAVA_HOME\lib\ext 目录中的或通过 java.ext.dirs 系统变量指定路径中的类库。应用程序类加载器(Application ClassLoader)负责加载用户路径classpath上的类库。JVM 通过双亲委派模型进行类的加载 也可以通过继承 java.lang.ClassLoader实现自定义的类加载器。
10.双亲委派
类收到了类加载请求他首先不会尝试自己去加载这个类而是把这个请求委派给父类去完成每一个层次类加载器都是如此因此所有的加载请求都应该传送到启动类加载其中只有当父类加载器反馈自己无法完成这个请求的时候在它的加载路径下没有找到所需加载的Class 子类加载器才会尝试自己去加载。好处是比如加载位于 rt.jar 包中的类 java.lang.Object不管是哪个加载器加载这个类最终都是委托给顶层的启动类加载器进行加载这样就保证了使用不同的类加载器最终得到的都是同样一个 Object 对象