当前位置: 首页 > news >正文

深圳网站公司哪家好WordPress链接公众号插件

深圳网站公司哪家好,WordPress链接公众号插件,小程序外包公司太坑了,百度关键词网站排名优化软件1、强软弱虚引用 强引用 当内存不足的时候#xff0c;JVM开始垃圾回收#xff0c;对于强引用的对象#xff0c;就算是出现了OOM也不会对该对象进行回收#xff0c;打死也不回收~#xff01; 强引用是我们最常见的普通对象引用#xff0c;只要还有一个强引用指向一个对象…1、强软弱虚引用 强引用 当内存不足的时候JVM开始垃圾回收对于强引用的对象就算是出现了OOM也不会对该对象进行回收打死也不回收~ 强引用是我们最常见的普通对象引用只要还有一个强引用指向一个对象就能表明对象还“活着”垃圾收集器不会碰这种对象。在Java中最常见的就是强引用 把一个对象赋给一个引用变量这个引用变量就是一个强引用。当一个对象被强引用变量引用时它处于可达状态它是不可能被垃圾回收机制回收的即使该对 象以后永远都不会被用到JVM也不会回收因此强引用是造成Java内存泄漏的主要原因之一。 public class StrongReferenceDemo {public static void main(String[] args) {// 这样定义的默认就是强应用Object obj1 new Object();// 使用第二个引用指向刚刚创建的Object对象Object obj2 obj1;// 置空obj1 null;// 垃圾回收System.gc();System.out.println(obj1);System.out.println(obj2);} }输出结果我们能够发现即使 obj1 被设置成了null然后调用gc进行回收但是也没有回收实例出来的对象obj2还是能够指向该地址也就是说垃圾回收器 并没有将该对象进行垃圾回收 null java.lang.Object14ae5a5软引用 软引用是一种相对弱化了一些的引用需要用Java.lang.ref.SoftReference类来实现可以让对象豁免一些垃圾收集对于只有软引用的对象来讲 当系统内存充足时它不会被回收当系统内存不足时它会被回收 软引用通常在对内存敏感的程序中比如高速缓存就用到了软引用内存够用的时候就保留不够用就回收 public class SoftReferenceDemo {/*** 内存够用的时候*/public static void softRefMemoryEnough() {// 创建一个强应用Object o1 new Object();// 创建一个软引用SoftReferenceObject softReference new SoftReference(o1);System.out.println(o1);System.out.println(softReference.get());o1 null;// 手动GCSystem.gc();System.out.println(o1);System.out.println(softReference.get());}/*** JVM配置故意产生大对象并配置小的内存让它的内存不够用了导致OOM看软引用的回收情况* -Xms5m -Xmx5m -XX:PrintGCDetails*/public static void softRefMemoryNoEnough() {System.out.println();// 创建一个强应用Object o1 new Object();// 创建一个软引用SoftReferenceObject softReference new SoftReference(o1);System.out.println(o1);System.out.println(softReference.get());o1 null;// 模拟OOM自动GCtry {// 创建30M的大对象byte[] bytes new byte[30 * 1024 * 1024];} catch (Exception e) {e.printStackTrace();} finally {System.out.println(o1);System.out.println(softReference.get());}}public static void main(String[] args) {softRefMemoryEnough();softRefMemoryNoEnough();} }我们写了两个方法一个是内存够用的时候一个是内存不够用的时候 我们首先查看内存够用的时候首先输出的是 o1 和 软引用的 softReference我们都能够看到值 然后我们把o1设置为null执行手动GC后我们发现softReference的值还存在说明内存充足的时候软引用的对象不会被回收 java.lang.Object14ae5a5 java.lang.Object14ae5a5[GC (System.gc()) [PSYoungGen: 1396K-504K(1536K)] 1504K-732K(5632K), 0.0007842 secs] [Times: user0.00 sys0.00, real0.00 secs] [Full GC (System.gc()) [PSYoungGen: 504K-0K(1536K)] [ParOldGen: 228K-651K(4096K)] 732K-651K(5632K), [Metaspace: 3480K-3480K(1056768K)], 0.0058450 secs] [Times: user0.00 sys0.00, real0.01 secs] null java.lang.Object14ae5a5下面我们看当内存不够的时候我们使用了JVM启动参数配置给初始化堆内存为5M -Xms5m -Xmx5m -XX:PrintGCDetails但是在创建对象的时候我们创建了一个30M的大对象 // 创建30M的大对象 byte[] bytes new byte[30 * 1024 * 1024];这就必然会触发垃圾回收机制这也是中间出现的垃圾回收过程最后看结果我们发现o1 和 softReference都被回收了因此说明软引用在内存不足的时候会自动回收 java.lang.Object7f31245a java.lang.Object7f31245a[GC (Allocation Failure) [PSYoungGen: 31K-160K(1536K)] 682K-811K(5632K), 0.0003603 secs] [Times: user0.00 sys0.00, real0.00 secs] [GC (Allocation Failure) [PSYoungGen: 160K-96K(1536K)] 811K-747K(5632K), 0.0006385 secs] [Times: user0.00 sys0.00, real0.00 secs] [Full GC (Allocation Failure) [PSYoungGen: 96K-0K(1536K)] [ParOldGen: 651K-646K(4096K)] 747K-646K(5632K), [Metaspace: 3488K-3488K(1056768K)], 0.0067976 secs] [Times: user0.02 sys0.00, real0.01 secs] [GC (Allocation Failure) [PSYoungGen: 0K-0K(1536K)] 646K-646K(5632K), 0.0004024 secs] [Times: user0.00 sys0.00, real0.00 secs] [Full GC (Allocation Failure) [PSYoungGen: 0K-0K(1536K)] [ParOldGen: 646K-627K(4096K)] 646K-627K(5632K), [Metaspace: 3488K-3488K(1056768K)], 0.0065506 secs] [Times: user0.00 sys0.00, real0.01 secs] null null弱引用 不管内存是否够只要有GC操作就会进行回收 弱引用需要用 java.lang.ref.WeakReference 类来实现它比软引用生存期更短 对于只有弱引用的对象来说只要垃圾回收机制一运行不管JVM的内存空间是否足够都会回收该对象占用的空间。 public class WeakReferenceDemo {public static void main(String[] args) {Object o1 new Object();WeakReferenceObject weakReference new WeakReference(o1);System.out.println(o1);System.out.println(weakReference.get());o1 null;System.gc();System.out.println(o1);System.out.println(weakReference.get());} }我们看结果能够发现我们并没有制造出OOM内存溢出而只是调用了一下GC操作垃圾回收就把它给收集了 java.lang.Object14ae5a5 java.lang.Object14ae5a5[GC (System.gc()) [PSYoungGen: 5246K-808K(76288K)] 5246K-816K(251392K), 0.0008236 secs] [Times: user0.00 sys0.00, real0.00 secs] [Full GC (System.gc()) [PSYoungGen: 808K-0K(76288K)] [ParOldGen: 8K-675K(175104K)] 816K-675K(251392K), [Metaspace: 3494K-3494K(1056768K)], 0.0035953 secs] [Times: user0.00 sys0.00, real0.00 secs] null null软引用和弱引用的使用场景 场景假如有一个应用需要读取大量的本地图片 如果每次读取图片都从硬盘读取则会严重影响性能如果一次性全部加载到内存中又可能造成内存溢出 此时使用软引用可以解决这个问题 设计思路使用HashMap来保存图片的路径和相应图片对象关联的软引用之间的映射关系在内存不足时JVM会自动回收这些缓存图片对象所占的空间从而有 效地避免了OOM的问题 MapString, SoftReferenceString imageCache new HashMapString, SoftReferenceBitmap();WeakHashMap是什么 比如一些常常和底层打交道的mybatis等底层都应用到了WeakHashMap WeakHashMap和HashMap类似只不过它的Key是使用了弱引用的也就是说当执行GC的时候WeakHashMap会被回收 public class WeakHashMapDemo {public static void main(String[] args) {myHashMap();System.out.println();myWeakHashMap();}private static void myHashMap() {MapInteger, String map new HashMap();Integer key new Integer(1);String value HashMap;map.put(key, value);System.out.println(map);key null;System.gc();System.out.println(map);}private static void myWeakHashMap() {MapInteger, String map new WeakHashMap();Integer key new Integer(1);String value WeakHashMap;map.put(key, value);System.out.println(map);key null;System.gc();System.out.println(map);} }虚引用 概念 虚引用又称为幽灵引用需要java.lang.ref.PhantomReference 类来实现 顾名思义就是形同虚设与其他几种引用都不同虚引用并不会决定对象的生命周期。 如果一个对象持有虚引用那么它就和没有任何引用一样在任何时候都可能被垃圾回收器回收它不能单独使用也不能通过它访问对象虚引用必须和引用队列 ReferenceQueue联合使用。 虚引用的主要作用和跟踪对象被垃圾回收的状态仅仅是提供一种确保对象被finalize以后做某些事情的机制。 PhantomReference的get方法总是返回null因此无法访问对象的引用对象。其意义在于说明一个对象已经进入finalization阶段可以被gc回收用来实现比 finalization机制更灵活的回收操作 换句话说设置虚引用关联的唯一目的就是在这个对象被收集器回收的时候收到一个系统通知或者后续添加进一步的处理Java技术允许使用finalize()方法在 垃圾收集器将对象从内存中清除出去之前做必要的清理工作 这个就相当于Spring AOP里面的后置通知 场景 一般用于在回收时候做通知相关操作 引用队列 ReferenceQueue 软引用弱引用虚引用在回收之前需要在引用队列保存一下 我们在初始化的弱引用或者虚引用的时候可以传入一个引用队列 Object o1 new Object();// 创建引用队列 ReferenceQueueObject referenceQueue new ReferenceQueue();// 创建一个弱引用 WeakReferenceObject weakReference new WeakReference(o1, referenceQueue);那么在进行GC回收的时候弱引用和虚引用的对象都会被回收但是在回收之前它会被送至引用队列中 完整代码如下 public class PhantomReferenceDemo {public static void main(String[] args) {Object o1 new Object();// 创建引用队列ReferenceQueueObject referenceQueue new ReferenceQueue();// 创建一个弱引用WeakReferenceObject weakReference new WeakReference(o1, referenceQueue);// 创建一个弱引用 // PhantomReferenceObject weakReference new PhantomReference(o1, referenceQueue);System.out.println(o1);System.out.println(weakReference.get());// 取队列中的内容System.out.println(referenceQueue.poll());o1 null;System.gc();System.out.println(执行GC操作);try {TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(o1);System.out.println(weakReference.get());// 取队列中的内容System.out.println(referenceQueue.poll());} }运行结果 java.lang.Object14ae5a5 java.lang.Object14ae5a5 null 执行GC操作 null null java.lang.ref.WeakReference7f3124从这里我们能看到在进行垃圾回收后我们弱引用对象也被设置成null但是在队列中还能够导出该引用的实例这就说明在回收之前该弱引用的实例被放 置引用队列中了我们可以通过引用队列进行一些后置操作 2、经典错误 JVM中常见的两个错误 StackoverFlowError 栈溢出 OutofMemoryError: java heap space堆溢出 除此之外还有以下的错误 java.lang.StackOverflowErrorjava.lang.OutOfMemoryErrorjava heap spacejava.lang.OutOfMemoryErrorGC overhead limit exceeededjava.lang.OutOfMemoryErrorDirect buffer memoryjava.lang.OutOfMemoryErrorunable to create new native threadjava.lang.OutOfMemoryErrorMetaspace 架构 OutOfMemoryError和StackOverflowError是属于Error不是Exception StackOverFlowError 堆栈溢出我们有最简单的一个递归调用就会造成堆栈溢出也就是深度的方法调用 栈一般是512K不断的深度调用直到栈被撑破 public class StackOverflowErrorDemo {public static void main(String[] args) {stackOverflowError();}/*** 栈一般是512K不断的深度调用直到栈被撑破* Exception in thread main java.lang.StackOverflowError*/private static void stackOverflowError() {stackOverflowError();} }运行结果 Exception in thread main java.lang.StackOverflowErrorat com.moxi.interview.study.oom.StackOverflowErrorDemo.stackOverflowError(StackOverflowErrorDemo.java:17)OutOfMemoryErrorjava heap space 创建了很多对象导致堆空间不够存储 public class JavaHeapSpaceDemo {public static void main(String[] args) {// 堆空间的大小 -Xms10m -Xmx10m// 创建一个 80M的字节数组byte [] bytes new byte[80 * 1024 * 1024];} }我们创建一个80M的数组会直接出现Java heap space Exception in thread main java.lang.OutOfMemoryError: Java heap spaceGC overhead limit exceeded GC回收时间过长时会抛出OutOfMemoryError过长的定义是超过了98%的时间用来做GC并且回收了不到2%的堆内存 连续多次GC都只回收了不到2%的极端情况下才会抛出。假设不抛出GC overhead limit 错误会造成什么情况呢 那就是GC清理的这点内存很快会再次被填满迫使GC再次执行这样就形成了恶性循环CPU的使用率一直都是100%而GC却没有任何成果。 Direct buffer memory 这是由于NIO引起的 写NIO程序的时候经常会使用ByteBuffer来读取或写入数据这是一种基于通道(Channel) 与 缓冲区(Buffer)的I/O方式它可以使用Native 函数库直接分配堆外内 存然后通过一个存储在Java堆里面的DirectByteBuffer对象作为这块内存的引用进行操作。这样能在一些场景中显著提高性能因为避免了在Java堆和Native堆中来回复制数据。 ByteBuffer.allocate(capability)第一种方式是分配JVM堆内存属于GC管辖范围由于需要拷贝所以速度相对较慢 ByteBuffer.allocteDirect(capability)第二种方式是分配OS本地内存不属于GC管辖范围由于不需要内存的拷贝所以速度相对较快 但如果不断分配本地内存堆内存很少使用那么JVM就不需要执行GCDirectByteBuffer对象就不会被回收这时候怼内存充足但本地内存可能已经使用光 了再次尝试分配本地内存就会出现OutOfMemoryError那么程序就奔溃了。 一句话说本地内存不足但是堆内存充足的时候就会出现这个问题 我们使用 -XX:MaxDirectMemorySize5m 配置能使用的堆外物理内存为5M -Xms10m -Xmx10m -XX:PrintGCDetails -XX:MaxDirectMemorySize5m然后我们申请一个6M的空间 // 只设置了5M的物理内存使用但是却分配 6M的空间 ByteBuffer bb ByteBuffer.allocateDirect(6 * 1024 * 1024);这个时候运行就会出现问题了 配置的maxDirectMemory5.0MB [GC (System.gc()) [PSYoungGen: 2030K-488K(2560K)] 2030K-796K(9728K), 0.0008326 secs] [Times: user0.00 sys0.00, real0.00 secs] [Full GC (System.gc()) [PSYoungGen: 488K-0K(2560K)] [ParOldGen: 308K-712K(7168K)] 796K-712K(9728K), [Metaspace: 3512K-3512K(1056768K)], 0.0052052 secs] [Times: user0.09 sys0.00, real0.00 secs] Exception in thread main java.lang.OutOfMemoryError: Direct buffer memoryat java.nio.Bits.reserveMemory(Bits.java:693)at java.nio.DirectByteBuffer.init(DirectByteBuffer.java:123)at java.nio.ByteBuffer.allocateDirect(ByteBuffer.java:311)at com.moxi.interview.study.oom.DIrectBufferMemoryDemo.main(DIrectBufferMemoryDemo.java:19)unable to create new native thread 导致原因 应用创建了太多线程一个应用进程创建多个线程超过系统承载极限 服务器并不允许你的应用程序创建这么多线程linux系统默认运行单个进程可以创建的线程为1024个如果应用创建超过这个数量就会报 java.lang.OutOfMemoryError:unable to create new native thread 解决方法 想办法降低你应用程序创建线程的数量分析应用是否真的需要创建这么多线程如果不是改代码将线程数降到最低对于有的应用确实需要创建很多线程远超过linux系统默认1024个线程限制可以通过修改linux服务器配置扩大linux默认限制 public class UnableCreateNewThreadDemo {public static void main(String[] args) {for (int i 0; ; i) {System.out.println(************** i i);new Thread(() - {try {TimeUnit.SECONDS.sleep(Integer.MAX_VALUE);} catch (InterruptedException e) {e.printStackTrace();}}, String.valueOf(i)).start();}} }这个时候就会出现下列的错误线程数大概在 900多个 Exception in thread main java.lang.OutOfMemoryError: unable to cerate new native threadlinux环境可以在如下配置文件中修改对应用户最大创建线程数量 /etc/security/limits.d/20-nproc.confMetaspace 元空间内存不足Matespace元空间应用的是本地内存 -XX:MetaspaceSize 的初始化大小为20M 元空间是什么 元空间就是我们的方法区存放的是类模板类信息常量池等 Metaspace是方法区HotSpot中的实现它与永久代最大的区别在于Metaspace并不在虚拟内存中而是使用本地内存也即在java8中class metadatathe virtual machines internal presentation of Java class被存储在叫做Matespace的native memory 永久代java8后背元空间Metaspace取代了存放了以下信息 虚拟机加载的类信息常量池静态变量即时编译后的代码 模拟Metaspace空间溢出我们不断生成类往元空间里灌输类占据的空间总会超过Metaspace指定的空间大小 代码 在模拟异常生成时候因为初始化的元空间为20M因此我们使用JVM参数调整元空间的大小为了更好的效果 -XX:MetaspaceSize8m -XX:MaxMetaspaceSize8m代码如下 public class MetaspaceOutOfMemoryDemo {// 静态类static class OOMTest {}public static void main(final String[] args) {// 模拟计数多少次以后发生异常int i 0;try {while (true) {i;// 使用Spring的动态字节码技术Enhancer enhancer new Enhancer();enhancer.setSuperclass(OOMTest.class);enhancer.setUseCache(false);enhancer.setCallback(new MethodInterceptor() {Overridepublic Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {return methodProxy.invokeSuper(o, args);}});}} catch (Exception e) {System.out.println(发生异常的次数: i);e.printStackTrace();} finally {}} }会出现以下错误 发生异常的次数: 201 java.lang.OutOfMemoryError:Metaspace3、垃圾收集器 GC垃圾回收算法和垃圾收集器关系 天上飞的理念要有落地的实现垃圾收集器就是GC垃圾回收算法的实现 GC算法是内存回收的方法论垃圾收集器就是算法的落地实现 GC算法主要有以下几种 引用计数几乎不用无法解决循环引用的问题复制拷贝用于新生代标记清除用于老年代标记整理用于老年代 因为目前为止还没有完美的收集器出现更没有万能的收集器只是针对具体应用最合适的收集器进行分代收集那个代用什么收集器 四种主要的垃圾收集器 Serial串行回收 -XX:UseSeriallGCParallel并行回收 -XX:UseParallelGCCMS并发标记清除G1ZGCjava 11 出现的 Serial 串行垃圾回收器它为单线程环境设计且值使用一个线程进行垃圾收集会暂停所有的用户线程只有当垃圾回收完成时才会重新唤醒主线程继续执行。所以不适合服务器环境 Parallel 并行垃圾收集器多个垃圾收集线程并行工作此时用户线程也是阻塞的适用于科学计算 / 大数据处理等弱交互场景也就是说Serial 和 Parallel其实是类似的不过是多了几个线程进行垃圾收集但是主线程都会被暂停但是并行垃圾收集器处理时间肯定比串行的垃圾收集器要更短 CMS 并发标记清除用户线程和垃圾收集线程同时执行不一定是并行可能是交替执行不需要停顿用户线程互联网公司都在使用适用于响应时间有要求的场景。并发是可以有交互的也就是说可以一边进行收集一边执行应用程序。 G1 G1垃圾回收器将堆内存分割成不同区域然后并发的进行垃圾回收 垃圾收集器总结 注意并行垃圾回收在单核CPU下可能会更慢 查看默认垃圾收集器 使用下面JVM命令查看配置的初始参数 -XX:PrintCommandLineFlags然后运行一个程序后能够看到它的一些初始配置信息 -XX:InitialHeapSize266376000 -XX:MaxHeapSize4262016000 -XX:PrintCommandLineFlags -XX:UseCompressedClassPointers -XX:UseCompressedOops -XX:-UseLargePagesIndividualAllocation -XX:UseParallelGC移动到最后一句就能看到 -XX:UseParallelGC 说明使用的是并行垃圾回收 -XX:UseParallelGC默认垃圾收集器有哪些 Java中一共有7大垃圾收集器 UserSerialGC串行垃圾收集器UserParallelGC并行垃圾收集器UseConcMarkSweepGCCMS并发标记清除UseParNewGC年轻代的并行垃圾回收器UseParallelOldGC老年代的并行垃圾回收器UseG1GCG1垃圾收集器UserSerialOldGC串行老年代垃圾收集器已经被移除 底层源码 各垃圾收集器的使用范围 新生代使用的 Serial Copying UserSerialGC串行垃圾回收器Parallel ScavengeUserParallelGC并行垃圾收集器ParNewUserParNewGC新生代并行垃圾收集器 老年区使用的 Serial OldUseSerialOldGC老年代串行垃圾收集器Parallel CompactingParallel OldUseParallelOldGC老年代并行垃圾收集器CMSUseConcMarkSwepp并行标记清除垃圾收集器 各区都能使用的 G1UseG1GCG1垃圾收集器 垃圾收集器就来具体实现这些GC算法并实现内存回收不同厂商不同版本的虚拟机实现差别很大HotSpot中包含的收集器如下图所示 部分参数说明 DefNewDefault New GenerationTenuredOldParNewParallel New GenerationPSYoungGenParallel ScavengeParOldGenParallel Old Generation Java中的Server和Client模式 使用范围一般使用Server模式Client模式基本不会使用 操作系统 32位的Window操作系统不论硬件如何都默认使用Client的JVM模式32位的其它操作系统2G内存同时有2个cpu以上用Server模式低于该配置还是Client模式64位只有Server模式 GC之Serial收集器 是一个单线程单线程的收集器在进行垃圾收集时候必须暂停其他所有的工作线程直到它收集结束。 串行收集器是最古老最稳定以及效率高的收集器只使用一个线程去回收但其在垃圾收集过程中可能会产生较长的停顿(Stop-The-World 状态)。 虽然在收集垃 圾过程中需要暂停所有其它的工作线程但是它简单高效对于限定单个CPU环境来说没有线程交互的开销可以获得最高的单线程垃圾收集效率因此Serial垃 圾收集器依然是Java虚拟机运行在Client模式下默认的新生代垃圾收集器 对应JVM参数是-XX:UseSerialGC 开启后会使用Serial(Young区用) Serial Old(Old区用) 的收集器组合 表示新生代、老年代都会使用串行回收收集器新生代使用复制算法老年代使用标记-整理算法 -Xms10m -Xmx10m -XX:PrintGCDetails -XX:PrintConmandLineFlags -XX:UseSerialGCGC之ParNew收集器 并行收集器使用多线程进行垃圾回收在垃圾收集会Stop-the-World暂停其他所有的工作线程直到它收集结束 ParNew收集器其实就是Serial收集器新生代的并行多线程版本最常见的应用场景时配合老年代的CMS GC工作其余的行为和Serial收集器完全一样ParNew垃 圾收集器在垃圾收集过程中同样也要暂停所有其他的工作线程。它是很多Java虚拟机运行在Server模式下新生代的默认垃圾收集器。 常见对应JVM参数-XX:UseParNewGC 启动ParNew收集器只影响新生代的收集不影响老年代 开启上述参数后会使用ParNewYoung区用 Serial Old的收集器组合新生代使用复制算法老年代采用标记-整理算法 -Xms10m -Xmx10m -XX:PrintGCDetails -XX:PrintConmandLineFlags -XX:UseParNewGC但是会出现警告即 ParNew 和 Serial Old 这样搭配Java8已经不再被推荐 备注-XX:ParallelGCThreads数字N 表示启动多少个GC线程 cpu8 N 5/8 cpu8 N实际个数GC之Parallel收集器 Parallel Scavenge收集器类似ParNew也是一个新生代垃圾收集器使用复制算法也是一个并行的多线程的垃圾收集器俗称吞吐量优先收集器。一句话串行 收集器在新生代和老年代的并行化。 它重点关注的是 可控制的吞吐量(Thoughput运行用户代码时间(运行用户代码时间垃圾收集时间),也即比如程序运行100分钟垃圾收集时间1分钟吞吐量就是99% )。高吞吐量 意味着高效利用CPU的时间它多用于在后台运算而不需要太多交互的任务。 自适应调节策略也是ParallelScavenge收集器与ParNew收集器的一个重要区别。(自适应调节策略:虚拟机会根据当前系统的运行情况收集性能监控信息动态调整 这些参数以提供最合适的停顿时间-XX:MaxGCPauseMillis或最大的吞吐量。 常用JVM参数-XX:UseParallelGC或-XX:UseParallelOldGC可互相激活使用Parallel Scanvenge收集器。 开启该参数后新生代使用复制算法老年代使用标记-整理算法 -Xms10m -Xmx10m -XX:PrintGCDetails -XX:PrintConmandLineFlags -XX:UseParallelGCGC之ParallelOld收集器 Parallel Old收集器是Parallel Scavenge的老年代版本使用多线程的标记-整理算法Parallel Old收集器在JDK1.6才开始提供。 在JDK1.6之前新生代使用ParallelScavenge收集器只能搭配老年代的Serial Old收集器只能保证新生代的吞吐量优先无法保证整体的吞吐量。在JDK1.6以前 (Parallel Scavenge Serial Old) Parallel Old正是为了在老年代同样提供吞吐量优先的垃圾收集器如果系统对吞吐量要求比较高JDK1.8后可以考虑新生代Parallel Scavenge和老年代Parallel Old 收集器的搭配策略。在JDK1.8及后Parallel Scavenge Parallel Old JVM常用参数 -XX UseParallelOldGC使用Parallel Old收集器设置该参数后新生代Parallel老年代 Parallel Old使用老年代并行收集器 -Xms10m -Xmx10m -XX:PrintGCDetails -XX:PrintConmandLineFlags -XX:UseParallelOldlGCGC之CMS收集器 CMS收集器Concurrent Mark Sweep并发标记清除是一种以最短回收停顿时间为目标的收集器 适合应用在互联网或者B/S系统的服务器上这类应用尤其重视服务器的响应速度希望系统停顿时间最短。 CMS非常适合堆内存大CPU核数多的服务器端应用也是G1出现之前大型应用的首选收集器。 Concurrent Mark Sweep并发标记清除并发收集低停顿并发指的是与用户线程一起执行 开启该收集器的JVM参数 -XX:UseConcMarkSweepGC 开启该参数后会自动将 -XX:UseParNewGC打开开启该参数后使用ParNew(young 区用 CMSOld 区用 Serial Old 的收集器组合Serial Old将作为CMS出错的后备收集器 -Xms10m -Xmx10m -XX:PrintGCDetails -XX:UseConcMarkSweepGC四个步骤 初始标记CMS initial mark 只是标记一个GC Roots 能直接关联的对象速度很快仍然需要暂停所有的工作线程 并发标记CMS concurrent mark和用户线程一起 进行GC Roots跟踪过程和用户线程一起工作不需要暂停工作线程。主要标记过程标记全部对象 重新标记CMS remark 为了修正在并发标记期间因用户程序继续运行而导致标记产生变动的那一部分对象的标记记录仍然需要暂停所有的工作线程由于并发标记时用户 线程依然运行因此在正式清理前在做修正 并发清除CMS concurrent sweep和用户线程一起 清除GC Roots不可达对象和用户线程一起工作不需要暂停工作线程。基于标记结果直接清理对象由于耗时最长的并发标记和并发清除过程中垃 圾收集线程可以和用户现在一起并发工作所以总体上来看CMS收集器的内存回收和用户线程是一起并发地执行。 优点并发收集低停顿 缺点并发执行对CPU资源压力大采用的标记清除算法会导致大量碎片 由于并发进行CMS在收集与应用线程会同时增加对堆内存的占用也就是说CMS必须在老年代堆内存用尽之前完成垃圾回收否则CMS回收失败时将触发担 保机制串行老年代收集器将会以STW方式进行一次GC从而造成较大的停顿时间 标记清除算法无法整理空间碎片老年代空间会随着应用时长被逐步耗尽最后将不得不通过担保机制对堆内存进行压缩CMS也提供了参数 -XX:CMSFullGCSBeForeCompaction默认0即每次都进行内存整理来指定多少次CMS收集之后进行一次压缩的Full GC GC之SerialOld收集器 Serial Old是Serial垃圾收集器老年代版本它同样是一个单线程的收集器使用标记-整理算法这个收集器也主要是运行在Client默认的Java虚拟机中默认的老年代垃圾收集器 在Server模式下主要有两个用途了解版本已经到8及以后 在JDK1.5之前版本中与新生代的Parallel Scavenge收集器搭配使用Parallel Scavenge Serial Old作为老年代版中使用CMS收集器的后备垃圾收集方案。 配置方法 -Xms10m -Xmx10m -XX:PrintGCDetails -XX:PrintConmandLineFlags -XX:UseSerialOldlGC该垃圾收集器目前已经不推荐使用了 垃圾收集器如何选择 组合的选择 单CPU或者小内存单机程序 -XX:UseSerialGC 多CPU需要最大的吞吐量如后台计算型应用 -XX:UseParallelGC这两个相互激活-XX:UseParallelOldGC 多CPU追求低停顿时间需要快速响应如互联网应用 -XX:UseConcMarkSweepGC-XX:ParNewGC 参数新生代垃圾收集器新生代算法老年代垃圾收集器老年代算法-XX:UseSerialGCSerialGC复制SerialOldGC标记整理-XX:UseParNewGCParNew复制SerialOldGC标记整理-XX:UseParallelGCParallel [Scavenge]复制Parallel Old标记整理-XX:UseConcMarkSweepGCParNew复制CMS Serial Old的收集器组合Serial Old作为CMS出错的后备收集器标记清除-XX:UseG1GCG1整体上采用标记整理算法局部复制 G1垃圾收集器 开启G1垃圾收集器 -XX:UseG1GC以前收集器的特点 年轻代和老年代是各自独立且连续的内存块年轻代收集使用单eden S0 S1 进行复制算法老年代收集必须扫描珍整个老年代区域都是以尽可能少而快速地执行GC为设计原则 G1是什么 G1Garbage-First 收集器是一款面向服务端应用的收集器应用在多处理器和大容量内存环境中在实现高吞吐量的同时尽可能满足垃圾收集暂停时间的要 求。另外它还具有一下特征 像CMS收集器一样能与应用程序并发执行整理空闲空间更快需要更多的时间来预测GC停顿时间不希望牺牲大量的吞吐量性能不需要更大的Java Heap G1收集器设计目标是取代CMS收集器它同CMS相比在以下方面表现的更出色 G1是一个有整理内存过程的垃圾收集器不会产生很多内存碎片。G1的Stop The WorldSTW更可控G1在停顿时间上添加了预测机制用户可以指定期望停顿时间。 CMS垃圾收集器虽然减少了暂停应用程序的运行时间但是它还存在着内存碎片问题。于是为了去除内存碎片问题同时又保留CMS垃圾收集器低暂停时间的优 点JAVA7发布了一个新的垃圾收集器-G1垃圾收集器 G1是在2012年才在JDK1.7中可用Oracle官方计划在JDK9中将G1变成默认的垃圾收集器以替代CMS它是一款面向服务端应用的收集器主要应用在多CPU和 大内存服务器环境下极大减少垃圾收集的停顿时间全面提升服务器的性能逐步替换Java8以前的CMS收集器 主要改变时EdenSurvivor和Tenured等内存区域不再是连续了而是变成一个个大小一样的region每个region从1M到32M不等。一个region有可能属于 EdenSurvivor或者Tenured内存区域。 特点 G1能充分利用多CPU多核环境硬件优势尽量缩短STW G1整体上采用标记-整理算法局部是通过复制算法不会产生内存碎片 宏观上看G1之中不再区分年轻代和老年代。把内存划分成多个独立的子区域Region可以近似理解为一个围棋的棋盘 G1收集器里面将整个内存区域都混合在一起了但其本身依然在小范围内要进行年轻代和老年代的区分保留了新生代和老年代但他们不再是物理隔离的 而是通过一部分Region的集合且不需要Region是连续的也就是说依然会采取不同的GC方式来处理不同的区域 G1虽然也是分代收集器但整个内存分区不存在物理上的年轻代和老年代的区别也不需要完全独立的Survivorto space堆做复制准备G1只有逻辑上的 分代概念或者说每个分区都可能随G1的运行在不同代之间前后切换。 底层原理 Region区域化垃圾收集器化整为零打破了原来新生区和老年区的壁垒避免了全内存扫描只需要按照区域来进行扫描即可。 区域化内存划片Region整体遍为了一些列不连续的内存区域避免了全内存区的GC操作。 核心思想是将整个堆内存区域分成大小相同的子区域Region在JVM启动时会自动设置子区域大小 在堆的使用上G1并不要求对象的存储一定是物理上连续的只要逻辑上连续即可每个分区也不会固定地为某个代服务可以按需在年轻代和老年代之间切 换。启动时可以通过参数-XX:G1HeapRegionSizen 可指定分区大小1MB~32MB且必须是2的幂默认将整堆划分为2048个分区。 大小范围在1MB~32MB最多能设置2048个区域也即能够支持的最大内存为32MB*2048 64G内存 Region区域化垃圾收集器 Region区域化垃圾收集器 G1将新生代、老年代的物理空间划分取消了 同时对内存进行了区域划分 G1算法将堆划分为若干个区域Reign它仍然属于分代收集器这些Region的一部分包含新生代新生代的垃圾收集依然采用暂停所有应用线程的方式将存 活对象拷贝到老年代或者Survivor空间 这些Region的一部分包含老年代G1收集器通过将对象从一个区域复制到另外一个区域完成了清理工作。这就意味着在正常的处理过程中G1完成了堆的压 缩至少是部分堆的压缩这样也就不会有CMS内存碎片的问题存在了。 在G1中还有一种特殊的区域叫做Humongous巨大的区域如果一个对象占用了空间超过了分区容量50%以上G1收集器就认为这是一个巨型对象这 些巨型对象默认直接分配在老年代但是如果他是一个短期存在的巨型对象就会对垃圾收集器造成负面影响为了解决这个问题G1划分了一个Humongous 区它用来专门存放巨型对象。如果一个H区装不下一个巨型对象那么G1会寻找连续的H区来存储为了能找到连续的H区有时候不得不启动Full GC。 回收步骤 针对Eden区进行收集Eden区耗尽后会被触发主要是小区域收集 形成连续的内存块避免内碎片 Eden区的数据移动到Survivor区加入出现Survivor区空间不够Eden区数据会晋升到Old区Survivor区的数据移动到新的Survivor区部分数据晋升到Old区最后Eden区收拾干净了GC结束用户的应用程序继续执行 回收完成后 小区域收集 形成连续的内存块最后在收集完成后就会形成连续的内存空间这样就解决了内存碎片的问题 四步过程 初始标记只标记GC Roots能直接关联到的对象并发标记进行GC Roots Tracing链路扫描的过程最终标记修正并发标记期间因为程序运行导致标记发生变化的那一部分对象筛选回收根据时间来进行价值最大化回收 参数配置 开发人员仅仅需要申明以下参数即可 三步归纳-XX:UseG1GC -Xmx32G -XX:MaxGCPauseMillis100 -XX:MaxGCPauseMillisn最大GC停顿时间单位毫秒这是个软目标JVM尽可能停顿小于这个时间 G1和CMS比较 G1不会产生内碎片是可以精准控制停顿。该收集器是把整个堆新生代、老年代划分成多个固定大小的区域每次根据允许停顿的时间去收集垃圾最多的区域。 4、linux服务问题诊断 top 使用top命令的话重点关注的是 %CPU、%MEM 、load average 三个指标 load average三个指标分别代表1、5、15分钟的负载情况 在这个命令下按1的话可以看到每个CPU的占用情况 uptime系统性能命令的精简版 vmstat 查看CPU包含但是不限于查看额外 查看所有CPU核信息mpstat -p ALL 2每个进程使用CPU的用量分解信息pidstat -u 1 -p 进程编号 命令格式vmstat -n 2 3 一般vmstat工具的使用是通过两个数字参数来完成的第一个参数是采样的时间间隔数单位秒第二个参数是采样的次数 r列表示运行和等待CPU时间片的进程数这个值如果长期大于系统CPU的个数说明CPU不足需要增加CPU。 b列表示在等待资源的进程数如正在等待I/O、内存交换等。 us列显示了用户进程消耗的CPU时间百分比此列是关注的重点。us的值比较高时说明用户进程消耗的CPU时间多如果长期大于50%就需要考虑优化程序或算法。 sy列显示了内核进程消耗的CPU时间百分比。sy的值较高时说明内核消耗的CPU资源很多。根据经验ussy的参考值为80%如果大于80%说明可能存在CPU资源不足的情况。 id列显示了CPU处在空闲状态的时间百分比。 wa列显示了I/O等待所占用的CPU时间百分比。wa值越高说明I/O等待越严重。根据经验wa的参考值为20%如果wa值超过20%说明I/O等待严重。 free free -h以人类能看懂的方式查看物理内存 free -m以MB为单位查看物理内存 free -g以GB为单位查看物理内存 df 格式df -h (-hhuman表示以人类能看到的方式换算) iostat 系统慢有两种原因引起的一个是CPU高一个是大量IO操作 格式iostat -xdk 2 3 磁盘块设备分布 rkB /s每秒读取数据量kB wkB/s每秒写入数据量kB svctm I/O请求的平均服务时间单位毫秒 await I/O请求的平均等待时间单位毫秒值越小性能越好 util一秒钟有百分几的时间用于I/O操作。接近100%时表示磁盘带宽跑满需要优化程序或者增加磁盘 rkB/swkB/s根据系统应用不同会有不同的值但有规律遵循长期、超大数据读写肯定不正常需要优化程序读取。 svctm的值与await的值很接近表示几乎没有I/O等待磁盘性能好如果await的值远高于svctm的值则表示I/O队列等待太长需要优化程序或更换更快磁盘 网络IOifstat CPU占用过高的定位分析思路 先用top命令找出CPU占比最高的 ps -ef或者jps进一步定位得知是一个怎么样的一个后台程序作搞屎棍 ps -mp 进程id -o THREAD,tid,time -m 显示所有的线程 -p pid进程使用cpu的时间-o 该参数后是用户自定义格式 将需要的线程ID转换为16进制格式英文小写格式命令printf %x 9298将9298转换为十六进制 jstack 进程ID | grep tid(16进制线程ID小写英文) -A60 演示 top jps ps -mp 4467 -o THREAD,tid,time printf %x 9298 jstack 4467 | grep 2452 -A100定位到大量占用cpu的程序在com.gykjit.spd.system.websocket.DataWebSocketServer的第47行检查源码发现有同事提交了死循环代码。 内存占用问题排查 启动参数添加这样如果发生oom报错就可以通过visualvm分析dump文件 -XX:HeapDumpOnOutOfMemoryError -XX:HeapDumpPath/data/tmp/heapdump.hprof未发生oom也可以手动生成堆快照 jmap -dump:live,formatb,file./heapdump.hprof 14988arthas 也可以在服务器上安装阿里arthas arthas官网 https://arthas.aliyun.com/doc/quick-start.html下载arthas-boot并通过java -jar arthas-boot.jar 启动 wget https://alibaba.github.io/arthas/arthas-boot.jar java -jar arthas-boot.jararthas-boot是Arthas的启动程序它启动后会列出所有的Java进程用户可以选择需要诊断的目标进程。 选择对应进程例如wm进程输入7 再Enter/回车 然后输入dashboard回车 5、字符串常量 String:intern()是一个本地方法它的作用是如果字符串常量池中已经包含一个等于此String对象的字符串则返回代表池中这个字符串的String对象的引用否 则会将此String对象包含的字符串添加到常量池中并且返回此String对象的引用。 public class ConstantsTest {public static void main(String[] args) {String str1 new StringBuilder(58).append(tongcheng).toString();System.out.println(str1 str1.intern());System.out.println();String str2 new StringBuilder(ja).append(va).toString();System.out.println(str2 str2.intern());System.out.println();String str3 new StringBuilder(58).append(tongcheng).toString();System.out.println(str3 str3.intern());} }运行结果 truefalsefalse通过执行结果可以判断出在这段代码执行前常量池中就有一个Java字符串。 类加载器和rt.jar - 根加载器提前部署加载rt.jar OpenJDK8源码 http://openjdk.java.net/ openjdk8\jdk\src\share\classes\sun\misc 考查点 - intern()方法判断true/false- 《深入理解java虚拟机》书原题是否读过经典JVM书籍 这段代码在JDK 6中运行会得到两个false而在JDK 7中运行会得到一个true和一个false。产生差异的原因是在JDK 6中intern()方法会把首次遇到的字符串实例复制到永久代的字符串常量池中存储返回的也是永久代里面这个字符串实例的引用而由StringBuilder创建的字符串对象实例在Java堆上所以必然不可能是同一个引用结果将返回false。而JDK 7(以及部分其他虚拟机例如JRockit的intern()方法实现就不需要再拷贝字符串的实例到永久代了既然字符串常量池已经移到Java堆中那只需要在常量池里记录一下首次出现的实例引用即可因此intern()返回的引用和由StringBuilder创建的那个字符串实例就是同一个。而对str2比较返回false这是因为“java”这个字符串在执行StringBuilder.toString()之前就已经出现过了字符串常量池中已经有它的引用不符合intern()方法要求“首次遇到”的原则“计算机软件这个字符串则是首次出现的因此结果返回true。sun.misc.Version类会在JDK类库的初始化过程中被加载并初始化而在初始化时它需要对静态常量字段根据指定的常量值(ConstantValue〉做默认初始化此时 被sun.misc.Version.launcher静态常量字段所引用的java字符串字面量就被intern到HotSpot VM的字符串常量池——StringTable里了。 6、LockSupport LockSupport是用来创建锁和其他同步类的基本线程阻塞原语。LockSupport中的park()和 unpark()的作用分别是阻塞线程和解除阻塞线程。总之比wait/notifyawait/signal更强。3种让线程等待和唤醒的方法方式1使用Object中的wait()方法让线程等待使用object中的notify()方法唤醒线程 方式2使用JUC包中Condition的await()方法让线程等待使用signal()方法唤醒线程 方式3LockSupport类可以阻塞当前线程以及唤醒指定被阻塞的线程传统的synchronized和Lock Object中的wait和notify方法必须要在同步块或者方法里面且成对出现使用否则会抛出java.lang.IllegalMonitorStateException。 调用顺序要先wait后notify Condition的await和signal方法必须要在同步块或者方法里面且成对出现使用否则会抛出java.lang.IllegalMonitorStateException。 调用顺序要先await后signal LockSupport LockSupport是用来创建锁和共他同步类的基本线程阻塞原语。 LockSuport是一个线程阻塞工具类所有的方法都是静态方法可以让线程在任意位置阻塞阻寨之后也有对应的唤醒方法。归根结底LockSupport调用的Unsafe中的native代码。 LockSupport提供park()和unpark()方法实现阻塞线程和解除线程阻塞的过程 LockSupport和每个使用它的线程都有一个许可(permit)关联。permit相当于10的开关默认是0 调用一次unpark就加1变成1 调用一次park会消费permit也就是将1变成0同时park立即返回。 如再次调用park会变成阻塞(因为permit为零了会阻塞在这里一直到permit变为1)这时调用unpark会把permit置为1。每个线程都有一个相关的permit, permit最多只有一个重复调用unpark也不会积累凭证。
http://www.hkea.cn/news/14533059/

相关文章:

  • 苏州网站设计都选苏州聚尚网络南昌网站建设好么
  • 学校网站建设培训方案网站图片代码怎么做的
  • 中国住房和城乡建设部网站首页海口seo计费
  • 电影网站源码怎么做的深圳网站设计 创同盟
  • 做门户网站的网络公司新塘网站建设
  • 做网站 卖产品静态网站
  • 南通seo网站诊断wordpress进不去仪表盘
  • 晋城门户网站建设网站网站建设设计
  • 网站按钮设计重新安装WordPress 如何备份
  • 太仓有做网站的地方吗丹东网络推广
  • 上海网站备案要求吗可玩儿小程序可以加盟么
  • 建设视频网站流量苏州企业网站
  • 做h5页面的网站蘑菇漯河网络科技有限公司
  • 东莞市建设质量监督网站哈尔滨市人社app
  • 网站制作 招聘某某公司网络营销策划书
  • .net网站如何优化网站备案成功后
  • 外贸机械网站设计开发流程
  • 新创建的网站网站开发公司会在最后面加上公司
  • 做网站都需要自己的服务器吗小程序登录不上去怎么办
  • 如何导入旧网站数据库优秀网站建设哪个公司好
  • 怎么查看网站服务器位置公司网站建设代理一般做多久
  • 与建设通相关的网站设计素材网站破解
  • 签约做网站模板next.js做纯静态网站
  • 如何选择企业建站公司企业咨询公司名称大全
  • 杭州下沙做网站的论坛关于做美食的网站
  • 制作一个网站需要多少钱中山网站建设哪家强
  • 阿里云clouder网站建设外贸网站导航栏建设技巧
  • 辽宁智能网站建设价位wordpress 突然502
  • 一般做网站什么价格电商网页设计图片
  • 网站建设规划书实训报告自学ui设计需要多久