深圳专业网站建设平台网页,百度网盘账号登录入口,wordpress免费汽车配件企业主题,线上引流推广怎么做1.1 内存管理
1.1.1 JVM内存区域 编号 名字 功能 备注 1 堆 主要用于存放新创建的对象 (所有对象都在这里分配内存) jdk1.8之后永久代被替换成为了元空间#xff08;Metaspace#xff09; 2 方法区(加、常、静、即) 被虚拟机加载的类信息(版本、字段、方法、接口…1.1 内存管理
1.1.1 JVM内存区域 编号 名字 功能 备注 1 堆 主要用于存放新创建的对象 (所有对象都在这里分配内存) jdk1.8之后永久代被替换成为了元空间Metaspace 2 方法区(加、常、静、即) 被虚拟机加载的类信息(版本、字段、方法、接口)、常量、静态变量、即时编译器编译后的代码等数据加常静即 运行时常量池是方法区的一部分。Class 文件中除了有类的版本、字段、方法、接口等描述信息外还有常量池信息用于存放编译期生成的各种字面量和符号引用方法中的基本类型本地变量将直接存储在工作内存的栈帧结构中 3 虚拟机栈线程私有动、方、操、局 动态链接方法出口操作数栈局部变量表动方操局 每个 Java 方法在执行的同时会创建一个栈帧用于存储局部变量表、操作数栈、常量池引用等信息。从方法调用直至执行完成的过程就对应着一个栈帧在 Java 虚拟机栈中入栈和出栈的过程。 4 本地方法栈线程私有 区别是 虚拟机栈为虚拟机执行 Java 方法 也就是字节码服务而本地方法栈则为虚拟机使用到的 Native 方法服务。 在 HotSpot 虚拟机中和 Java 虚拟机栈合二为一。 5 程序计数器线程私有 程序计数器主要有下面两个作用1字节码解释器通过改变程序计数器来依次读取指令从而实现代码的流程控制如顺序执行、选择、循环、异常处理。在多线程的情况下2程序计数器用于记录当前线程执行的位置从而当线程被切换回来的时候能够知道该线程上次运行到哪儿了。 1.2 垃圾回收机制
1.2.1 对象已死判断
1引用计数法
给对象中添加一个引用计数器每当有一个地方引用它计数器就加 1当引用失效计数器就减 1任何时候计数器为 0 的对象就是不可能再被使用的。这个方法实现简单效率高但是目前主流的虚拟机中并没有选择这个算法来管理内存其最主要的原因是它很难解决对象之间相互循环引用的问题。
2可达性分析
这个算法的基本思想就是通过一系列的称为GC Roots的对象作为起点从这些节点开始向下搜索节点所走过的路径称为引用链当一个对象到 GC Roots 没有任何引用链相连的话则证明此对象是不可用的。
3强引用、软引用、弱引用、虚引用
强引用我们使用的大部分引用实际上都是强引用这是使用最普遍的引用。如果一个对象具有强引用那就类似于必不可少的生活用品垃圾回收器绝不会回收它。当内存空 间不足Java 虚拟机宁愿抛出 OutOfMemoryError 错误使程序异常终止也不会靠随意回收具有强引用的对象来解决内存不足问题。软引用如果内存空间足够垃圾回收器就不会回收它如果内存空间不足了就会回收这些对象的内存。只要垃圾回收器没有回收它该对象就可以被程序使用。软引用可用来实现内存敏感的高速缓存。弱引用弱引用与软引用的区别在于只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它 所管辖的内存区域的过程中一旦发现了只具有弱引用的对象不管当前内存空间足够与否都会回收它的内存。不过由于垃圾回收器是一个优先级很低的线程 因此不一定会很快发现那些只具有弱引用的对象。弱引用可以和一个引用队列ReferenceQueue联合使用如果弱引用所引用的对象被垃圾回收Java 虚拟机就会把这个弱引用加入到与之关联的引用队列中。虚引用与其他几种引用都不同虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用那么它就和没有任何引用一样在任何时候都可能被垃圾回收。
1.2.2 垃圾回收算法
1复制算法
为了解决效率问题“复制”收集算法出现了。它可以将内存分为大小相同的两块每次使用其中的一块。当这一块的内存使用完后就将还存活的对象复制到另一块去然后再把使用的空间一次清理掉。这样就使每次的内存回收都是对内存区间的一半进行回收。
2标记清除
该算法分为“标记”和“清除”阶段首先标记出所有需要回收的对象在标记完成后统一回收所有被标记的对象。它是最基础的收集算法后续的算法都是对其不足进行改进得到。这种垃圾收集算法会带来两个明显的问题
效率问题
空间问题标记清除后会产生大量不连续的碎片
3标记整理
根据老年代的特点特出的一种标记算法标记过程仍然与“标记-清除”算法一样但后续步骤不是直接对可回收对象回收而是让所有存活的对象向一端移动然后直接清理掉端边界以外的内存。
4新生代和老年代
根据对象存活周期的不同将内存分为几块。一般将 java 堆分为新生代和老年代这样我们就可以根据各个年代的特点选择合适的垃圾收集算法。比如在新生代中每次收集都会有大量对象死去所以可以选择复制算法只需要付出少量对象的复制成本就可以完成每次垃圾收集。而老年代的对象存活几率是比较高的而且没有额外的空间对它进行分配担保所以我们必须选择“标记-清除”或“标记-整理”算法进行垃圾收集。
分代收集算法HotSpot 虚拟机 GC 采用分代收集算法
根据对象存活周期的不同将内存划分为几块。一般是把 Java 堆分为新生代和老年代根据年代的特点来选择最佳的收集算法。
新生代复制算法老年代标记-整理算法
堆大小新生代老年代默认分别占堆空间为1/3、2/3新生代又被分为Eden、from survivor、to survivor默认8:1:1
这样划分是为了更好的管理堆内存中的对象方便 GC 算法来进行垃圾回收。 对象的分配通常在 Eden 中大对象需要大量连续内存空间的 Java 对象如很长的字符串或数据直接进入老年代-XX:PretenureSizeThreshold。
当 Eden 区满后会触发 Minor GC把 Eden 区和 from survivor 区中存活的对象进行转移其中到达年龄经过多次Minor GC的会被放入老年代未到达年龄的放入 to survivor 区。
然后清空 Eden 区和 from survivor 区交换 from survivor 与 to survivor 的名字。
若存活对象大于 to survivor 区容量则会被直接放入老年代。若打开了自适应-XX:AdaptiveSizePolicyGC会自动重新调整新生代大小。
若老年代满了则触发 Full GC。 5Minor GC vs Major GC/Full GC
Minor GC回收新生代包括 Eden 和 Survivor 区域因为 Java 对象大多都具备朝生夕灭的特性所以 Minor GC 非常频繁一般回收速度也比较快。Major GC / Full GC: 回收老年代出现了 Major GC经常会伴随至少一次的 Minor GC但这并非绝对。Major GC 的速度一般会比 Minor GC 慢 10 倍 以上。
在 JVM 规范中Major GC 和 Full GC 都没有一个正式的定义所以有人也简单地认为 Major GC 清理老年代而 Full GC 清理整个内存堆。 1.2.3 垃圾回收器 编号 回收器 算法 步骤 备注 1 Serial 收集器串行 单CPU的client模式 2 ParNew 收集器并行 它是许多运行在Server 模式下的虚拟机的首要选择除了 Serial 收集器外只有它能与 CMS 收集器配合工作。 3 Parallel Scavenge 收集器并行 吞吐量Parallel Scavenge 收集器关注点是吞吐量高效率的利用 CPU后台运行不需要太多交互 4 Serial Old 收集器串行 单CPU的client模式和CMS配合 5 Parallel Old 收集器并行 吞吐量后台运行不需要太多交互 6 CMS 收集器并发 1初始标记 2并发标记混合 3重新标记 4并发清除混合 CMS 等垃圾收集器的关注点更多的是用户线程的停顿时间提高用户体验集中使用在互联网或者B/S系统服务端 7 G1 收集器并发 面向服务端将来替换CMS
1.2.4 FULL GC时机 情况 分类 旧生代空间不足 旧生代空间只有在新生代对象转入及创建为大对象、大数组时才会出现不足的现象当执行Full GC后空间仍然不足则抛出如下错误 java.lang.OutOfMemoryError: Java heap space 为避免以上两种状况引起的FullGC调优时应尽量做到让对象在Minor GC阶段被回收、让对象在新生代多存活一段时间及不要创建过大的对象及数组。 Permanet Generation空间满 PermanetGeneration中存放的为一些class的信息等当系统中要加载的类、反射的类和调用的方法较多时Permanet Generation可能会被占满在未配置为采用CMS GC的情况下会执行Full GC。如果经过Full GC仍然回收不了那么JVM会抛出如下错误信息 java.lang.OutOfMemoryError: PermGen space 为避免Perm Gen占满造成Full GC现象可采用的方法为增大Perm Gen空间或转为使用CMS GC。 CMS GC时出现promotion failed和concurrent mode failure 对于采用CMS进行旧生代GC的程序而言尤其要注意GC日志中是否有promotion failed和concurrent mode failure两种状况当这两种状况出现时可能会触发Full GC。 promotionfailed是在进行Minor GC时survivor space放不下、对象只能放入旧生代而此时旧生代也放不下造成的concurrent mode failure是在执行CMS GC的过程中同时有对象要放入旧生代而此时旧生代空间不足造成的。 应对措施为增大survivorspace、旧生代空间或调低触发并发GC的比率但在JDK 5.0、6.0的版本中有可能会由于JDK的bug29导致CMS在remark完毕后很久才触发sweeping动作。对于这种状况可通过设置-XX:CMSMaxAbortablePrecleanTime5单位为ms来避免。 统计得到的Minor GC晋升到旧生代的平均大小大于旧生代的剩余空间 这是一个较为复杂的触发情况Hotspot为了避免由于新生代对象晋升到旧生代导致旧生代空间不足的现象在进行Minor GC时做了一个判断如果之前统计所得到的Minor GC晋升到旧生代的平均大小大于旧生代的剩余空间那么就直接触发Full GC。 例如程序第一次触发MinorGC后有6MB的对象晋升到旧生代那么当下一次Minor GC发生时首先检查旧生代的剩余空间是否大于6MB如果小于6MB则执行Full GC。 当新生代采用PSGC时方式稍有不同PS GC是在Minor GC后也会检查例如上面的例子中第一次Minor GC后PS GC会检查此时旧生代的剩余空间是否大于6MB如小于则触发对旧生代的回收。 除了以上4种状况外对于使用RMI来进行RPC或管理的Sun JDK应用而言默认情况下会一小时执行一次Full GC。可通过在启动时通过- java-Dsun.rmi.dgc.client.gcInterval3600000来设置Full GC执行的间隔时间或通过-XX: DisableExplicitGC来禁止RMI调用System.gc。 手动调用System.gc()方法 System.gc() 建议JVM进行Full GC虽然只是建议但是很多情况下还是会触发Full GC建议能不使用此方法就别使用让虚拟机自己去管理它的内存可通过通过-XX: DisableExplicitGC来禁止RMI调用System.gc()。 1.3 类加载机制
1.3.1 类文件结构
魔文常访当字方属 编号 数据 解释 1 魔数 每个 Class 文件的头四个字节称为魔数Magic Number,它的唯一作用是确定这个文件是否为一个能被虚拟机接收的 Class 文件。 2 Class文件版本 高版本的 Java 虚拟机可以执行低版本编译器生成的 Class 文件但是低版本的 Java 虚拟机不能执行高版本编译器生成的 Class 文件。所以我们在实际开发的时候要确保开发的的 JDK 版本和生产环境的 JDK 版本保持一致。 3 常量池 常量池主要存放两大常量字面量和符号引用。字面量比较接近于 Java 语言层面的的常量概念如文本字符串、声明为 final 的常量值等。javap -v *.class查看 4 访问标志 在常量池结束之后紧接着的两个字节代表访问标志这个标志用于识别一些类或者接口层次的访问信息包括这个 Class 是类还是接口是否为 public 或者 abstract 类型如果是类的话是否声明为 final 等等。 5 当前类索引父类索引和接口索引集合 类索引用于确定这个类的全限定名父类索引用于确定这个类的父类的全限定名由于 Java 语言的单继承所以父类索引只有一个除了 java.lang.Object 之外所有的 java 类都有父类因此除了 java.lang.Object 外所有 Java 类的父类索引都不为 0。接口索引集合用来描述这个类实现了那些接口这些被实现的接口将按implents(如果这个类本身是接口的话则是extends) 后的接口顺序从左到右排列在接口索引集合中。 6 字段表集合 字段表field info用于描述接口或类中声明的变量。字段包括类级变量以及实例变量但不包括在方法内部声明的局部变量。 7 方法表集合 Class 文件存储格式中对方法的描述与对字段的描述几乎采用了完全一致的方式。方法表的结构如同字段表一样依次包括了访问标志、名称索引、描述符索引、属性表集合几项。 8 属性表集合 在 Class 文件字段表方法表中都可以携带自己的属性表集合以用于描述某些场景专有的信息。与 Class 文件中其它的数据项目要求的顺序、长度和内容不同属性表集合的限制稍微宽松一些不再要求各个属性表具有严格的顺序并且只要不与已有的属性名重复任何人实现的编译器都可以向属性表中写 入自己定义的属性信息Java 虚拟机运行时会忽略掉它不认识的属性。 1.3.2 对象的创建过程
加分初设执, 双亲委派模型
1类加载检查
虚拟机遇到一条 new 指令时首先将去检查这个指令的参数是否能在常量池中定位到这个类的符号引用并且检查这个符号引用代表的类是否已被加载过、解析和初始化过。如果没有那必须先执行相应的类加载过程。
类加载过程加验准解初
加载 protected Class? loadClass(String name, boolean resolve)是线程安全的 通过全类名获取定义此类的二进制字节流将字节流所代表的静态存储结构转换为方法区的运行时数据结构在内存中生成一个代表该类的Class 对象,作为方法区这些数据的访问入口验证文元字符 文件格式验证元数据验证字节码验证符号引用验证准备准备阶段是正式为类变量分配内存并设置类变量初始值的阶段解析解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程。解析动作主要针对类或接口、字段、类方法、接口方法、方法类型、方法句柄和调用限定符7类符号引用进行。初始化初始化是类加载的最后一步也是真正执行类中定义的 Java 程序代码(字节码)初始化阶段是执行类构造器 clinit ()方法的过程。
加载器
BootstrapClassLoader(启动类加载器) 最顶层的加载类由C实现负责加载 %JAVA_HOME%/lib目录下的jar包和类或者或被 -Xbootclasspath参数指定的路径中的所有类。ExtensionClassLoader(扩展类加载器) 主要负责加载目录 %JRE_HOME%/lib/ext 目录下的jar包和类或被 java.ext.dirs 系统变量所指定的路径下的jar包。AppClassLoader(应用程序类加载器) :面向我们用户的加载器负责加载当前应用classpath下的所有jar包和类。
每一个类都有一个对应它的类加载器。系统中的 ClassLoder 在协同工作的时候会默认使用 双亲委派模型 。即在类加载的时候系统会首先判断当前类是否被加载过。已经被加载的类会直接返回否则才会尝试加载。加载的时候首先会把该请求委派该父类加载器的 loadClass() 处理因此所有的请求最终都应该传送到顶层的启动类加载器 BootstrapClassLoader 中。当父类加载器无法处理时才由自己来处理。当父类加载器为null时会使用启动类加载器 BootstrapClassLoader 作为父类加载器。 双亲委派模型保证了Java程序的稳定运行可以避免类的重复加载JVM 区分不同类的方式不仅仅根据类名相同的类文件被不同的类加载器加载产生的是两个不同的类也保证了 Java 的核心 API 不被篡改。如果不用没有使用双亲委派模型而是每个类加载器加载自己的话就会出现一些问题比如我们编写一个称为 java.lang.Object 类的话那么程序运行的时候系统就会出现多个不同的 Object 类。
2分配内存
在类加载检查通过后接下来虚拟机将为新生对象分配内存。对象所需的内存大小在类加载完成后便可确定为对象分配空间的任务等同于把一块确定大小的内存从 Java 堆中划分出来。分配方式有 “指针碰撞” 和 “空闲列表”两种选择那种分配方式由 Java 堆是否规整决定而Java堆是否规整又由所采用的垃圾收集器是否带有压缩整理功能决定。
3初始化零值
内存分配完成后虚拟机需要将分配到的内存空间都初始化为零值不包括对象头这一步操作保证了对象的实例字段在 Java 代码中可以不赋初始值就直接使用程序能访问到这些字段的数据类型所对应的零值。
4设置对象头
元数据信息对象的哈希码对象的GC分代信息
初始化零值完成之后虚拟机要对对象进行必要的设置例如这个对象是那个类的实例、如何才能找到类的元数据信息、对象的哈希码、对象的 GC 分代年龄等信息。 这些信息存放在对象头中。 另外根据虚拟机当前运行状态的不同如是否启用偏向锁等对象头会有不同的设置方式。
5执行init方法
在上面工作都完成之后从虚拟机的视角来看一个新的对象已经产生了但从 Java 程序的视角来看对象创建才刚开始init 方法还没有执行所有的字段都还为零。所以一般来说执行 new 指令之后会接着执行 init 方法把对象按照程序员的意愿进行初始化这样一个真正可用的对象才算完全产生出来。 1.4 内存模型与线程
1.4.1 JMM概述
Java内存模型简称JMMJava Memory Model是Java虚拟机所定义的一种抽象规范用来屏蔽不同硬件和操作系统的内存访问差异让java程序在各种平台下都能达到一致的内存访问效果。
具体说来JVM中存在一个主存区Main Memory或Java Heap Memory对于所有线程进行共享而每个线程又有自己的工作内存Working Memory实际上是一个虚拟的概念工作内存中保存的是主存中某些变量的拷贝线程对所有变量的操作并非发生在主存区而是发生在工作内存中而线程之间是不能直接相互访问的变量在程序中的传递是依赖主存来完成的。
JMM描述的是一组规则围绕原子性、有序性和可见性展开
线程只能直接操作工作内存中的变量不同线程之间的变量值传递需要通过主内存来完成。 操作
lock锁定作用于主内存的变量把一个变量标识为一条线程独占的状态。unclock解锁作用于主内存的变量把一个处于锁定的状态释放出来。read读取作用于主内存的变量把一个变量的值从主内存传输到线程的工作内存中load载入作用于工作内存的变量把read操作从主内存 得到的变量值放入工作内存的变量副本中。use使用作用于工作内存的变量把工作内存中一个变量的值传递给执行引擎每当虚拟机遇到一个需要使用到变量的 值的字节码指令时将会执行这个操作。assign赋值作用于工作内存的变量把一个从执行引擎接收到的值 赋值给工作内存的变量每当虚拟机遇到一个给变 量赋值的字节码指令时执行这个操作。store存储作用于工作内存的变量把工作内存中的一个变量的值传递到主内存以便write操作使用。write写入作用于主内存的变量把store操作从工作内存中得到的变量的值放入主内存的变量中。
1.4.2 内存模型三大特性
1原子性
Java 内存模型保证了 read、write、load、use、assign、store、lock 和 unlock 操作具有原子性
2可见性
可见性指当一个线程修改了共享变量的值其它线程能够立即得知这个修改。Java 内存模型是通过在变量修改后将新值同步回主内存在变量读取前从主内存刷新变量值来实现可见性的。JMM 内部的实现通常是依赖于所谓的内存屏障通过禁止某些重排序的方式提供内存可见性保证也就是实现了各种 happen-before 规则。与此同时更多复杂度在于需要尽量确保各种编译器、各种体系结构的处理器都能够提供一致的行为。
3有序性
有序性是指在本线程内观察所有操作都是有序的。在一个线程观察另一个线程所有操作都是无序的无序是因为发生了指令重排序。在 Java 内存模型中允许编译器和处理器对指令进行重排序重排序过程不会影响到单线程程序的执行却会影响到多线程并发执行的正确性。 1.5 JDK监控和故障处理工具
故障排查https://www.cnblogs.com/stateis0/p/9062196.html
线上 CPU 飚高问题大家应该都遇到过那么如何定位问题呢
思路首先找到 CPU 飚高的那个 Java 进程因为你的服务器会有多个 JVM 进程。然后找到那个进程中的 “问题线程”最后根据线程堆栈信息找到问题代码。最后对代码进行排查,如何操作呢
通过 top 命令找到 CPU 消耗最高的进程并记住进程 ID。再次通过 top -Hp [进程 ID] 找到 CPU 消耗最高的线程 ID并记住线程 ID.通过 JDK 提供的 jstack 工具 dump 线程堆栈信息到指定文件中。具体命令jstack -l [进程 ID] jstack.log。由于刚刚的线程 ID 是十进制的而堆栈信息中的线程 ID 是16进制的因此我们需要将10进制的转换成16进制的并用这个线程 ID 在堆栈中查找。使用 printf %x\n [十进制数字] 可以将10进制转换成16进制。通过刚刚转换的16进制数字从堆栈信息里找到对应的线程堆栈。就可以从该堆栈中看出端倪。 编号 工具 解释 用法 1 jps (JVM Process Status: 类似 UNIX 的 ps 命令。用户查看所有 Java 进程的启动类、传入参数和 Java 虚拟机参数等信息 2 jinfo (Configuration Info for Java) : Configuration Info forJava,显示虚拟机配置信息; 1jinfo -flag MaxHeapSize 3 jstat (JVM Statistics Monitoring Tool): 用于收集 HotSpot 虚拟机各方面的运行数据; 4 jmap (Memory Map for Java) :生成堆栈储快照; 1可以使用-XX:HeapDumpOnOutOfMemoryError代替 5 jhat (JVM Heap Dump Browser ) : 用于分析 heapdump 文件它会建立一个 HTTP/HTML 服务器让用户可以在浏览器上查看分析结果; 6 jstack (Stack Trace for Java):生成虚拟机当前时刻的线程快照线程快照就是当前虚拟机内每一条线程正在执行的方法堆栈的集合。 7 jconsole 8 javap 查看字节码