JVM - 垃圾回收篇笔记 世界关注

垃圾回收

如何判断对象已死

引用计数法

早期Python虚拟机采用


(资料图片)

当对象被引用时,引用计数器++,取消引用时--

无法解决问题:相互循环引用

可达性分析算法

通过一系列成为GC Roots的根对象作为起始节点集,从GCRoots开始向下搜索,搜索的路径为”引用链“,如果没有引用链,也就是不可达,说明对象不可能再被使用

在Java技术体系中,固定可作为GCRoots的对象包括:

在JVMStack中引用的对象在方法区中类静态属性引用的变量在方法区中常量引用的对象在本地方法栈中JNI引用的对象JVM内部引用,基本数据类型对应的Class对象、常驻异常对象、系统类加载器所有被同步锁所持有的对象反应JVM内部情况的JMXBean、JVMTI中注册的回调、本地代码缓存等根据所选择的垃圾收集器、当前回收区域,采用其他对象临时性的加入

MAT

jmap -dump:format=b,live,file=11.bin

引用

强、软(SoftReference)、弱(WeakReference)

软引用:

必须配合引用队列:虚(PPhantomRefernce)、终结(FinalReference)

终结:使用finallize方法

引用队列关联

回收对象

利用可达性算法,不可达进行第一次标记。

标记后进行筛选:是否有必要执行finalize()方法,如果没有重写或者已经被调用过,那么就视为没有必要执行

如果有必要执行,那么会放入一个F-Queued的队列(引用队列)中,并在稍后由一条由虚拟机自动建立的,低调度优先级的Finalizer线程去执行他们的finalize()方法。

为了防止出现阻塞,F-Queue不会等待方法执行结束,否则会让其他对象永久处于等待,造成内存回收子系统 崩溃。

如果finalize()成功执行,重新于引用链上任一对象建立关系即可:比如把this赋值给某个类变量或者对象的成员变量,那么在第二次标记时,他将会被移出”即将回收“的集合。如果此时对象仍然没有逃脱,那么就要被回收了。

Systen.gc()还是系统自动回收,都只会调用一次finalize()方法

回收方法区

主要回收两个内容:废弃常量不再使用类型

判断一个常量是否废弃相对简单:该常量曾经进入常量池中,但现在又没有一个对象的值和该常量相同,也就是说没有对象引用该常量,且虚拟机中也没有其他地方引用该字面量,则该常量可以被垃圾回收,清理出常量池。

类、方法、字段的符号引用也类似。

但是判断一个类是否废弃需要满足下面三个条件:

该类的所有实例都已被回收(包括派生类)加载该类的类加载器已经被回收,这个条件除非是经过精心设计的可替换类加载器的场景,如OSGi、JSP的重加载等,否则通常很难达成该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过u哦反射访问该类的方法。

对于是否被零星回收,HotSpot提供了 -Xnoclassgc 参数进行控制,还可以使用 -XX:+TraceClassLoading、-XX:TraceClassUnLoading查看类加载和写在信息(前两个可以在Product版JVM中使用,最后一种需要FastDebug版的JVM支持)。

垃圾回收算法

标记清除

优点:记录垃圾碎片的起止地址,快速,不用清零缺点:碎片较多

标记整理

优点:没有内存碎片缺点:速度慢

复制算法

优点:不会有内存碎片需要占用双倍内存空间

分代回收

根据对象生命周期的不同特点分为新生代和老年代

新生代:处理不常用的对象

老年代:处理常用的对象

在新生代中,每次垃圾收集时都发现有大批对象死去,而每次回收后存活的少量对象将会逐步晋升到老年代释放

新生代垃圾回收

第一次垃圾回收

新创建的对象选择伊甸园空间

当伊甸园空间满了以后会进行垃圾护手,新生代的垃圾回收叫做Minor GC,回收后将存活对象赋值到幸存区,并将寿命+1

再将to和form交换指向位置

第二次垃圾回收

在第一次的回收基础上,对幸存区的某一个进行取消引用

此时to中的1已经被取消引用,所以直接删除

最后from和to交换位置、放入新对象

当年龄达到一定阙值后放入老年代

老年代垃圾回收

当新生代内存不够时,会尝试促发FullGC进行老年代的垃圾回收,准确来说是整个堆空间

总结

对象首先非陪在伊甸园区域新生代空间不足时,出发MinorGC,伊甸园和from存活的对象使用copy复制到to中,存活的对象年龄+1并且交换fromtoMinor GC 会引发 stop the world (暂停 其他用户正在执行的线程 wait,等垃圾回收结束后,用户线程才恢复运行 notifyAll) 当对象寿命超过阙值时,会晋升至老年代,最大寿命是15(4bit,但是不同的垃圾回收器不同)当老年代空间不足,会先尝试促发MinorGC,如果空间仍不足,那么触发Full GC,STW暂停时间(老年代回收时间)更长

GC相关参数

GC分析

-Xms20M -Xmx20M -Xmn10M -XX:+UseSerialGC -XX:+PrintGCDetails -verbose:gc

代码:

package org.example;import java.io.IOException;public class Main {    public static int a=5;    public static void main(String[] args) throws InterruptedException {        Thread.sleep(10000000000l);        return ;    }}

E:F:T=8:1:1,其中T默认是已使用内存

既然刚启动,那为什么EdenSpace会有占用呢?

类加载过程

思考:当new的对象占用内存如果超过了幸存区内存会发生什么?内存溢出?动态占比分配?试验代码:package org.example; import java.io.IOException; import java.lang.reflect.Array; import java.util.ArrayList; public class Main { public static int a=5; public static void main(String[] args) throws InterruptedException, IOException { System.in.read(); ArrayList bytes=new ArrayList<>(); System.out.println("添加对象中"); bytes.add(new byte1024*1024*5); bytes.forEach(System.out::println); System.in.read(); bytes.add(new byte1024*1024*5); bytes.forEach(System.out::println); System.in.read(); bytes.add(new byte1024*1024*5); bytes.forEach(System.out::println); bytes.add(new byte1024*1024*5); bytes.forEach(System.out::println); return ; } }结果:D:\Program\jdk\bin\java.exe -Xms20M -Xmx20M -Xmn10M -XX:+UseSerialGC -XX:+PrintGCDetails -verbose:gc "-javaagent:D:\Program\IntelliJ IDEA 2022.3.1\lib\idea_rt.jar=61656:D:\Program\IntelliJ IDEA 2022.3.1\bin" -Dfile.encoding=UTF-8 -classpath H:\JVM\gc\target\classes org.example.Main 0.003sgc -XX:+PrintGCDetails is deprecated. Will use -Xlog:gc* instead. 0.010sgc Using Serial 0.010sgc,init Version: 17.0.1+12-39 (release) 0.010sgc,init CPUs: 8 total, 8 available 0.010sgc,init Memory: 13810M 0.010sgc,init Large Page Support: Disabled 0.010sgc,init NUMA Support: Disabled 0.010sgc,init Compressed Oops: Enabled (32-bit) 0.010sgc,init Heap Min Capacity: 20M 0.010sgc,init Heap Initial Capacity: 20M 0.010sgc,init Heap Max Capacity: 20M 0.010sgc,init Pre-touch: Disabled 0.010sgc,metaspace CDS archive(s) mapped at: [0x0000000800000000-0x0000000800bc0000-0x0000000800bc0000), size 12320768, SharedBaseAddress: 0x0000000800000000, ArchiveRelocationMode: 0. 0.011sgc,metaspace Compressed class space mapped at: 0x0000000800c00000-0x0000000840c00000, reserved size: 1073741824 0.011sgc,metaspace Narrow klass base: 0x0000000800000000, Narrow klass shift: 0, Narrow klass range: 0x100000000 10.160sgc,start GC(0) Pause Young (Allocation Failure) 10.165sgc,heap GC(0) DefNew: 8192K(9216K)->1023K(9216K) Eden: 8192K(8192K)->0K(8192K) From: 0K(1024K)->1023K(1024K) 10.165sgc,heap GC(0) Tenured: 0K(10240K)->1443K(10240K) 10.165sgc,metaspace GC(0) Metaspace: 2679K(2880K)->2679K(2880K) NonClass: 2400K(2496K)->2400K(2496K) Class: 279K(384K)->279K(384K) 10.165sgc GC(0) Pause Young (Allocation Failure) 8M->2M(19M) 4.780ms 10.165sgc,cpu GC(0) User=0.00s Sys=0.02s Real=0.01s 10.384sgc,start GC(1) Pause Young (Allocation Failure) 10.390sgc,heap GC(1) DefNew: 9215K(9216K)->482K(9216K) Eden: 8192K(8192K)->0K(8192K) From: 1023K(1024K)->482K(1024K) 10.390sgc,heap GC(1) Tenured: 1443K(10240K)->2463K(10240K) 10.390sgc,metaspace GC(1) Metaspace: 4881K(5120K)->4881K(5120K) NonClass: 4357K(4480K)->4357K(4480K) Class: 523K(640K)->523K(640K) 10.390sgc GC(1) Pause Young (Allocation Failure) 10M->2M(19M) 5.666ms 10.390sgc,cpu GC(1) User=0.00s Sys=0.00s Real=0.01s 26.252sgc,start GC(2) Pause Full (System.gc()) 26.252sgc,phases,start GC(2) Phase 1: Mark live objects 26.257sgc,phases GC(2) Phase 1: Mark live objects 5.380ms 26.257sgc,phases,start GC(2) Phase 2: Compute new object addresses 26.259sgc,phases GC(2) Phase 2: Compute new object addresses 2.149ms 26.259sgc,phases,start GC(2) Phase 3: Adjust pointers 26.262sgc,phases GC(2) Phase 3: Adjust pointers 3.118ms 26.263sgc,phases,start GC(2) Phase 4: Move objects 26.263sgc,phases GC(2) Phase 4: Move objects 0.855ms 26.264sgc,heap GC(2) DefNew: 8083K(9216K)->0K(9216K) Eden: 7600K(8192K)->0K(8192K) From: 482K(1024K)->0K(1024K) 26.264sgc,heap GC(2) Tenured: 2463K(10240K)->3900K(10240K) 26.264sgc,metaspace GC(2) Metaspace: 7956K(8256K)->7956K(8256K) NonClass: 7083K(7232K)->7083K(7232K) Class: 872K(1024K)->872K(1024K) 26.264sgc GC(2) Pause Full (System.gc()) 10M->3M(19M) 12.256ms 26.264sgc,cpu GC(2) User=0.02s Sys=0.00s Real=0.01s 添加对象中 [B@3b07d329 49.675sgc,start GC(3) Pause Young (Allocation Failure) 49.678sgc,heap GC(3) DefNew: 8192K(9216K)->35K(9216K) Eden: 8192K(8192K)->0K(8192K) From: 0K(1024K)->35K(1024K) 49.678sgc,heap GC(3) Tenured: 3900K(10240K)->9020K(10240K) 49.678sgc,metaspace GC(3) Metaspace: 8137K(8384K)->8137K(8384K) NonClass: 7263K(7360K)->7263K(7360K) Class: 874K(1024K)->874K(1024K) 49.678sgc GC(3) Pause Young (Allocation Failure) 11M->8M(19M) 2.957ms 49.678sgc,cpu GC(3) User=0.00s Sys=0.02s Real=0.00s 96.458sgc,start GC(4) Pause Young (Allocation Failure) 96.458sgc GC(4) Pause Young (Allocation Failure) 14M->14M(19M) 0.078ms 96.458sgc,cpu GC(4) User=0.00s Sys=0.00s Real=0.00s 96.458sgc,start GC(5) Pause Full (Allocation Failure) 96.458sgc,phases,start GC(5) Phase 1: Mark live objects 96.467sgc,phases GC(5) Phase 1: Mark live objects 8.235ms 96.467sgc,phases,start GC(5) Phase 2: Compute new object addresses 96.469sgc,phases GC(5) Phase 2: Compute new object addresses 2.466ms 96.469sgc,phases,start GC(5) Phase 3: Adjust pointers 96.473sgc,phases GC(5) Phase 3: Adjust pointers 4.087ms 96.473sgc,phases,start GC(5) Phase 4: Move objects 96.474sgc,phases GC(5) Phase 4: Move objects 0.946ms 96.475sgc,heap GC(5) DefNew: 5571K(9216K)->0K(9216K) Eden: 5535K(8192K)->0K(8192K) From: 35K(1024K)->0K(1024K) 96.475sgc,heap GC(5) Tenured: 9020K(10240K)->8999K(10240K) 96.475sgc,metaspace GC(5) Metaspace: 8213K(8448K)->8092K(8448K) NonClass: 7339K(7424K)->7244K(7424K) Class: 874K(1024K)->848K(1024K) 96.475sgc GC(5) Pause Full (Allocation Failure) 14M->8M(19M) 16.586ms 96.475sgc,cpu GC(5) User=0.02s Sys=0.00s Real=0.02s [B@3b07d329 [B@682a0b20 96.477sgc,start GC(6) Pause Young (Allocation Failure) 96.477sgc GC(6) Pause Young (Allocation Failure) 13M->13M(19M) 0.086ms 96.477sgc,cpu GC(6) User=0.00s Sys=0.00s Real=0.00s 96.477sgc,start GC(7) Pause Full (Allocation Failure) 96.477sgc,phases,start GC(7) Phase 1: Mark live objects 96.485sgc,phases GC(7) Phase 1: Mark live objects 8.022ms 96.485sgc,phases,start GC(7) Phase 2: Compute new object addresses 96.487sgc,phases GC(7) Phase 2: Compute new object addresses 1.451ms 96.487sgc,phases,start GC(7) Phase 3: Adjust pointers 96.491sgc,phases GC(7) Phase 3: Adjust pointers 4.088ms 96.491sgc,phases,start GC(7) Phase 4: Move objects 96.492sgc,phases GC(7) Phase 4: Move objects 0.856ms 96.492sgc,heap GC(7) DefNew: 5217K(9216K)->5121K(9216K) Eden: 5217K(8192K)->5121K(8192K) From: 0K(1024K)->0K(1024K) 96.492sgc,heap GC(7) Tenured: 8999K(10240K)->8962K(10240K) 96.492sgc,metaspace GC(7) Metaspace: 8097K(8448K)->8028K(8448K) NonClass: 7247K(7424K)->7189K(7424K) Class: 849K(1024K)->838K(1024K) 96.492sgc GC(7) Pause Full (Allocation Failure) 13M->13M(19M) 15.333ms 96.492sgc,cpu GC(7) User=0.02s Sys=0.00s Real=0.02s 96.492sgc,start GC(8) Pause Full (Allocation Failure) 96.492sgc,phases,start GC(8) Phase 1: Mark live objects 96.498sgc,phases GC(8) Phase 1: Mark live objects 6.004ms 96.498sgc,phases,start GC(8) Phase 2: Compute new object addresses 96.500sgc,phases GC(8) Phase 2: Compute new object addresses 1.182ms 96.500sgc,phases,start GC(8) Phase 3: Adjust pointers 96.504sgc,phases GC(8) Phase 3: Adjust pointers 4.837ms 96.505sgc,phases,start GC(8) Phase 4: Move objects 96.507sgc,phases GC(8) Phase 4: Move objects 2.185ms 96.507sgc,heap GC(8) DefNew: 5121K(9216K)->5121K(9216K) Eden: 5121K(8192K)->5121K(8192K) From: 0K(1024K)->0K(1024K) 96.507sgc,heap GC(8) Tenured: 8962K(10240K)->8450K(10240K) 96.507sgc,metaspace GC(8) Metaspace: 8028K(8448K)->8028K(8448K) NonClass: 7189K(7424K)->7189K(7424K) Class: 838K(1024K)->838K(1024K) 96.507sgc GC(8) Pause Full (Allocation Failure) 13M->13M(19M) 14.864ms 96.507sgc,cpu GC(8) User=0.02s Sys=0.00s Real=0.02s 96.509sgc,heap,exit Heap 96.509sgc,heap,exit def new generation total 9216K, used 5405K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000) 96.509sgc,heap,exit eden space 8192K, 65% used [0x00000000fec00000, 0x00000000ff1475c8, 0x00000000ff400000) 96.509sgc,heap,exit from space 1024K, 0% used [0x00000000ff500000, 0x00000000ff500000, 0x00000000ff600000) 96.509sgc,heap,exit to space 1024K, 0% used [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000) 96.509sgc,heap,exit tenured generation total 10240K, used 8450K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000) 96.509sgc,heap,exit the space 10240K, 82% used [0x00000000ff600000, 0x00000000ffe40b90, 0x00000000ffe40c00, 0x0000000100000000) 96.509sgc,heap,exit Metaspace used 8034K, committed 8448K, reserved 1056768K 96.509sgc,heap,exit class space used 839K, committed 1024K, reserved 1048576K Exception in thread "main" java.lang.OutOfMemoryError: Java heap space at org.example.Main.main(Main.java:21) 进程已结束,退出代码1

最开始放到新生代,但是当继续new的时候,直接放入了老年代.

重复实验,发现如果幸存区空间充足,对象会放入幸存区并且延寿,否则直接将待延寿对象晋升到老年代,当老年代空间不足时,抛出oom异常.

package org.example; import java.io.IOException; import java.lang.reflect.Array; import java.util.ArrayList; public class Main { public static int a=5; public static void main(String[] args) throws InterruptedException, IOException { System.in.read(); ArrayList bytes=new ArrayList<>(); System.out.println("添加对象中"); bytes.add(new byte1024*1024*5); bytes.forEach(System.out::println); System.in.read(); bytes.add(new byte1024*1024*8); } }

添加对象中 [B@3b07d329 34.804sgc,start GC(4) Pause Young (Allocation Failure) 34.806sgc,heap GC(4) DefNew: 8192K(9216K)->14K(9216K) Eden: 8192K(8192K)->0K(8192K) From: 0K(1024K)->14K(1024K) 34.806sgc,heap GC(4) Tenured: 3897K(10240K)->9017K(10240K) 34.806sgc,metaspace GC(4) Metaspace: 8072K(8320K)->8072K(8320K) NonClass: 7196K(7296K)->7196K(7296K) Class: 875K(1024K)->875K(1024K) 34.806sgc GC(4) Pause Young (Allocation Failure) 11M->8M(19M) 2.195ms 34.806sgc,cpu GC(4) User=0.00s Sys=0.00s Real=0.00s 59.066sgc,start GC(5) Pause Young (Allocation Failure) 59.066sgc GC(5) Pause Young (Allocation Failure) 11M->11M(19M) 0.075ms 59.066sgc,cpu GC(5) User=0.00s Sys=0.00s Real=0.00s 59.066sgc,start GC(6) Pause Full (Allocation Failure) 59.066sgc,phases,start GC(6) Phase 1: Mark live objects 59.074sgc,phases GC(6) Phase 1: Mark live objects 7.218ms 59.074sgc,phases,start GC(6) Phase 2: Compute new object addresses 59.076sgc,phases GC(6) Phase 2: Compute new object addresses 1.923ms 59.076sgc,phases,start GC(6) Phase 3: Adjust pointers 59.080sgc,phases GC(6) Phase 3: Adjust pointers 3.998ms 59.080sgc,phases,start GC(6) Phase 4: Move objects 59.080sgc,phases GC(6) Phase 4: Move objects 0.713ms 59.082sgc,heap GC(6) DefNew: 3000K(9216K)->0K(9216K) Eden: 2985K(8192K)->0K(8192K) From: 14K(1024K)->0K(1024K) 59.082sgc,heap GC(6) Tenured: 9017K(10240K)->9000K(10240K) 59.082sgc,metaspace GC(6) Metaspace: 8199K(8448K)->8078K(8448K) NonClass: 7323K(7424K)->7229K(7424K) Class: 875K(1024K)->849K(1024K) 59.082sgc GC(6) Pause Full (Allocation Failure) 11M->8M(19M) 15.316ms 59.082sgc,cpu GC(6) User=0.02s Sys=0.00s Real=0.01s 59.082sgc,start GC(7) Pause Full (Allocation Failure) 59.082sgc,phases,start GC(7) Phase 1: Mark live objects 59.090sgc,phases GC(7) Phase 1: Mark live objects 7.740ms 59.090sgc,phases,start GC(7) Phase 2: Compute new object addresses 59.091sgc,phases GC(7) Phase 2: Compute new object addresses 1.085ms 59.091sgc,phases,start GC(7) Phase 3: Adjust pointers 59.094sgc,phases GC(7) Phase 3: Adjust pointers 3.000ms 59.094sgc,phases,start GC(7) Phase 4: Move objects 59.095sgc,phases GC(7) Phase 4: Move objects 1.331ms 59.095sgc,heap GC(7) DefNew: 0K(9216K)->0K(9216K) Eden: 0K(8192K)->0K(8192K) From: 0K(1024K)->0K(1024K) 59.095sgc,heap GC(7) Tenured: 9000K(10240K)->8452K(10240K) 59.095sgc,metaspace GC(7) Metaspace: 8078K(8448K)->8009K(8448K) NonClass: 7229K(7424K)->7171K(7424K) Class: 849K(1024K)->838K(1024K) 59.095sgc GC(7) Pause Full (Allocation Failure) 8M->8M(19M) 13.684ms 59.096sgc,cpu GC(7) User=0.02s Sys=0.00s Real=0.01s 59.097sgc,heap,exit Heap 59.097sgc,heap,exit def new generation total 9216K, used 220K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000) 59.097sgc,heap,exit eden space 8192K, 2% used [0x00000000fec00000, 0x00000000fec37390, 0x00000000ff400000) 59.097sgc,heap,exit from space 1024K, 0% used [0x00000000ff500000, 0x00000000ff500000, 0x00000000ff600000) 59.097sgc,heap,exit to space 1024K, 0% used [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000) 59.097sgc,heap,exit tenured generation total 10240K, used 8452K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000) 59.097sgc,heap,exit the space 10240K, 82% used [0x00000000ff600000, 0x00000000ffe41268, 0x00000000ffe41400, 0x0000000100000000) 59.097sgc,heap,exit Metaspace used 8013K, committed 8448K, reserved 1056768K 59.097sgc,heap,exit class space used 839K, committed 1024K, reserved 1048576K Exception in thread "main" java.lang.OutOfMemoryError: Java heap space at org.example.Main.main(Main.java:17) 进程已结束,退出代码1

大对象_oom

刚刚想到问题并实现了,下面就是这个类似的.?

没错,这种情况如果老年代空间足够(前提是新生代不够用)直接晋升

如果超过老年代的话,很明显就是OOM,不过会提前做个自救工作,GC

线程中OOM

当某个线程发生OOM时,会影响主线程吗?

无论时哪个线程,只要是一个进程,那么堆内存是共享的但是当一个线程抛出异常后,JVM会释放的该线程所占用的内存资源

综上,不会影响

垃圾回收器

串行 —— Serial

单线程堆内存较小,适合个人电脑
-XX:+uSEsERIALgc=Serial+SerialOld

Serial:作用于新生代,标记复制

SerialOld:作用于老年代,标记整理

吞吐量优先

吞吐量:运行用户代码时间/(运行用户代码时间+运行垃圾收集时间)

多线程堆内存较大,多核cpu让单位时间内,stw的时间最短
#JDK8下默认开启-XX:+UseParallelGC ~ -XX:+UseParallelOldGC#需要手动开启#自适应新生代大小-XX:+UseAdaptiveSizePolicy#根据目标调整吞吐量目标,如果达不到则调整堆的大小 1/(1+ratio) => 垃圾回收时间/总的运行时间,结果表示每100分钟执行n分钟-XX:GCTimeRatio=ratio#GC暂停时间-XX:MaxGCPauseMillis=ms-XX:ParallelGCThreads=n

默认线程数为cpu核数

响应优先 —— CMS 并发标记清除收集器

多线程堆内存较大,多核cpu尽可能让单次stop onworld(stw)的时间的最短

并行和并发

这里本来是OS的内容,在这里再提一下

并行
并发(Concurrent)
并行+并发

响应优先

-XX:+UseConcMarkSweepGc-XX:+UserParNewGC#老年代补救-XX:SerialOld#并行线程数,CPU核数-XX:ParallelGCThreads=n#并发线程数,一般设置为并行线程数的1/4-XX:ConGCThreads=threads-XX:CMSInitiatingOccupancyFraction=parent-XX:+CMSScavengeBeforeRemark
初始标记:只标记与GCRoots能直接关联到的对象并发标记:从GCRoots关联对象开始遍历整个图重新标记:修复并发标记期间,用户程序运作而导致标记产生变动的那部分对象的标记记录清除阶段:清楚标记阶段判断的已死亡对象

缺点

虽然不会导致用户线程停顿,但是导致应用程序变慢,降低总吞吐量CMS默认启动的回收线程数是$$(处理器核心数量+3)/4$$

,也就是说当核心数≥4时,并发回收垃圾手机线程只占用不超过25%的处理器运算资源,并且伴随着核心数增多而下降

当核心数<4时,对用户线程的影响变大 扩展:增量式并发收集器无法处理 浮动垃圾 ,有可能出现CMF(并发mode失败)从而导致另一次STW的Full GC产生

HotSpot算法细节实现

根节点枚举

GC Roots主要在全局性的引用和执行上下文中,且目前Java应用越做越庞大,类、常量很多,逐个检查耗费时间较长。

所有收集器根节点枚举必须STW可达性分析算法耗时最长的查找引用链过程已经可以做到与用户线程一起并发根节点枚举必须在保证一致性的快照中才得以进行——枚举期间,根节点对象的引用关系不发生变化

目前主流的虚拟机均采用准确式垃圾收集(虚拟机可以知道内存中某个位置的数据具体是什么类型),当用户线程停顿下来以后,并不需要一个不漏地检查完所有执行上下文和全局的应用位置,虚拟机应当时又办法直接得到哪些地方存放着用户引用的。

在HotSpot中,使用一组称为OopMap的数据结构来解决准确式垃圾回收的问题,在类加载动作完成的时候,虚拟机会把对象内什么偏移量上是什么类型的数据计算出来,在即使编译过程中,也会在特定位置记录栈、寄存器中哪些位置是引用。

安全点

OopMap协助虚拟机快速准确完成GC Roots的枚举,但是可能导致引用关系变化,换句话说

导致OopMap内容发生变化的指令非常多,如果每个指令都生成对应的OopMap,那么会需要大量的额外存储空间,这样垃圾收集伴随的空间成本将无比高昂

实际上HotSpot的确没有为每条指令生成OopMap,只是在特定的位置记录了这些信息你,这些位置被称为安全点

安全点的设定,决定了用户程序执行时并非在代码指定流的任意位置都能停下来进行垃圾收集,必须到达安全点才能暂停。

安全点的选定

不能太少,也不能让收集器的等待时间过长不能太频繁增大运行时的内存负荷以”是否具有让程序长时间执行的特征“为标准进行选定
- 指令序列复用
- 方法调用    - 循环跳转    - 异常跳转

安全区域

安全点机制只能保证程序执行时,在不太长的时间内就会遇到可进入垃圾手机的过程的安全点。

不执行的时候呢?

——没有分配处理器时间 Sleep or Blocked,此时线程无法响应虚拟机中断请求,不能再走到安全的地方终端挂起自己,虚拟机也显然不可能持续等待线程重新杯激活分配处理器时间。

为了解决这一问题,引入了安全区域(Safe Region)

安全区域是指能够确保在某一段代码片段中,引用关系不会发生变化,因此在这个区域中任意地方开始垃圾收集都是安全的。(背扩展拉伸了的安全点)

当用户线程执行刀安全区域里的代码时,首先会标识自己已经进入了安全区域,虚拟机在进行垃圾收集时不必去管此类线程,当线程离开安全区域时,检测虚拟机是否已经完成了根节点枚举(或垃圾收集过程中其他需要暂停用户线程的阶段),如果完成,线程就当作没事发生过继续执行,否则一直等待,知道收到可以离开安全区域的信号为止

记忆集与卡表

分代收集理论提到了为解决 对象跨代引用所带来的问题,垃圾收集器在新生代中建立了名为记忆集的数据结构-

记忆集

用于避免把整个老年代加进GCRoots扫描范围事实上不只是新生代、老年代之间才可有跨代引用的问题,所有设计部分区域收集(Partial GC)行为的垃圾收集器(G1,ZGC,Shenandoah)都会面临相同的问题记忆集是一种用于记录从非收集区域指向收集区域的指针集合的抽象数据结构。
- 如果不考虑成本,最简单的实现 可以用非收集区域中所有含跨代引用的对象数组来实现这个数据结构Class RemebereSetd{     Object[] setOBJECT_INTERGKENERATIONAL_REFRENCE_SIZE }

全部记录,无论是空间占用还是维护成本都相当高昂。

其实在垃圾收集的场景中,收集器只需要通过记忆集判断出某一块非收集区域是否存在指向了收集区域的指针即可。

在设计记忆集的时候,可以选择更为粗犷的记录粒度来节省记忆集的存储和维护成本。

字长精度每个记录精确刀一个字长(处理器寻址位数,32/64 bit,该精度决定了机器访问物理内存地址的指针长度),该字包含跨代指针对象精度每个记录精确到一个对象,该对象里有字段包含跨代指针卡精度 每个记录一块内存区域,该区域内有对象含有跨代指针其中“卡精度”采用卡表的方式实现记忆集,目前最常用。

卡表对记忆集的实现:

定义了记忆集的记录精度定义了与堆内存的映射关系

卡表与记忆集的关系,类似于HashMap和Map的关系

HotSpot中卡表的形式

最简单的一种形式:字节数组,一下代码时默认的卡表标记逻辑

CARD_TABLE [this address >>9] = 0;

该数组的每一个元素都对应(注意是对应,也就是说存放的是地址)着其标志的内容区域中一块特定大小的内存块,该内存块成为“卡页”。

一般来说 卡页 的大小都是 2^n字节数,HotSpot使用的卡页是2^9,也就是512字节(地址>>9 == 地址/512)

如果卡表标识内存区域的其实地址是0x0000的话,数组CARD_TABLE的第0、1、2号元素,分别对应了地址范围为0.00000x01FF、0x02000x03FF、0x04FF~0x05FF的卡页内存块.(每块大小为512)

一个卡页的内存中通常包含不止一个对象,只要卡页内有一个(或更多)对象的字段存在着跨代指针,那就将对应卡表的数组元素的值标识为1,成这个元素变脏(Dirty),没有则标识为0。在垃圾收集发生时,只要筛选出卡表中变脏的元素,就能轻易得出哪些卡页内存块中包含跨代指针,把他们加入GC Roots中一并扫描。

写屏障

写屏障用来解决卡表元素维护问题。

卡表元素何时变脏在其他分代区中对象引用了本区域对象时,其对应卡表元素就应该变脏变脏时间点原则上应该发生在引用类型字段赋值的那一刻,而处理这类问题就要用到写屏障。

写屏障可以看作在虚拟机层面对”引用类型字段赋值“这个动作的AOP切面,在引用对象负值时产生一个环形通知,供程序进行额外动作,因此写屏障分为写前屏障与写后屏障(将环绕通知看作前置通知+后置通知),HotSpot虚拟机很多哦收集器都有使用到写屏障,但是直到G1收集器出现之前,都只用到了写后屏障。

// 写后屏障更新卡表void oop_field_store(oop* filed,oop new_value){    //引用字段赋值操作    *field = new_value;    //写后屏障,在这里完成卡表状态更新    post_write_barrier(field,new_value);}

应用写屏障后,虚拟机就会为所有赋值操作生成相对应的指令,一旦收集器在写屏障中增加了更新卡表操作,无论更新的是不是老年代对新生代对象的引用,每次只要是对引用进行更新,都会产生额外的开销,不过和Minor GC是扫描整个老年代相比还是较小。

高并发环境下

在高并发场景下,卡表还面临”伪共享“问题

伪共享(False Sharing)

处理并发底层细节时一种经常需要考虑的问题,现代代中央处理器的缓存系统中以缓存行(Cache Line)为单位存储,当线程修改互相独立的变量时,如果这些变量恰好共享同一个缓存行,就会彼此影响(写回、无效化、同步)而导致性能降低,这就是伪共享问题

伪共享相关文章: JAVA系列:缓存一致性协议( MESI协议)_NIO4444的博客-CSDN博客伪共享(False Sharing) - 知乎 (zhihu.com)真实字节二面:什么是伪共享? - 知乎 (zhihu.com) Java 伪共享的原理深度解析以及避免方法_刘Java的博客-CSDN博客【深入理解JVM】3、CPU储存器+MESI+CPU伪共享+CPU乱序问题及代码论证+volatile+synchrnized【面试必备】_Hello-zhou的博客-CSDN博客

避免伪共享问题:

不采用无条件的写屏障,先检查卡表标记,只有当该卡表元素未被标记时才将其标记变脏if( CARD_TABLE this address >>9 !=0 ){ CARD_TABLE this address >>9=0; }在JDK7之后,HotSpot虚拟机增加了一个新的参数 -XX:+UseCondCardMark,用来决定是否开启卡表更新条件的判断。开启会增加一次额外判断的开销,但能够避免伪共享问题,是否打开要根据实际运行情况来进行测试权衡。

G1(Garbage First)

使用场景:

同时注重吞吐量、低延迟,默认的暂停目标时200超大堆内存,会将堆划分为多个大小相等的Region整体上时标记+整理算法,两个对象之间是复制算法

相关JVM参数

-XX:+UseG1GC-XX:G1HeapRegionSize=size-XX:MaxGCPauseMillis=time

G1回收阶段

G1新生代回收

标签: 海外加速 编程算法

上一篇 :

下一篇 :

JVM - 垃圾回收篇笔记 世界关注

通过一系列成为GCRoots的根对象作为起始节点集,从GCRoots开始向下搜索,搜索的路径为”引用链“,如果...

腾讯云 02-25 14:13:11

小快克

1、小快克是海南亚洲制药股份有限公司的商标品牌。文章到此就分享结束,希望对大家有所帮助。

互联网 02-25 13:13:10

苏州举办首届中小学优秀外籍教师表彰大会 环球热闻

苏州举办首届中小学优秀外籍教师表彰大会

扬眼 02-25 11:11:22

世界今热点:基金经理离任潮后遗症:“顶流”走了业绩难保 基民还能持有吗?

2023年,浩浩荡荡的基金经理离职潮还在继续,不到两个月,年内已有209位基金经理从349只基金中卸任,有...

华夏时报 02-25 09:04:05

四代火影叫什么

1、波风水门,日本漫画《火影忍者》系列及衍生作品中的男性角色。2、火之国木叶隐村的第四代火影,木叶...

互联网 02-25 07:56:43

交通事故伤残划分的依据是什么?

人体损害伤残评定的依据是《人体损伤致残程度分级》。

法师兄 02-25 05:00:39

黄仁烨个人资料简介_黄仁烨个人资料_焦点快报

1、英文名In-yeopHwang简体中文名黄仁烨出生地韩国出生日期1991-01-19星座摩羯座家庭成员父母 弟

互联网 02-25 00:14:57

天天最新:藤子 f 不二雄

1、藤子不二雄,日本漫画家组合,是两位漫画家联合创作时所使用的笔名。2、他们是藤本弘和安孙子素雄。3...

互联网 02-24 22:51:52

当心粮食陷阱!中国7天进口100万吨美国大豆,苏联的教训不能忘 世界实时

当心粮食陷阱!中国7天进口100万吨美国大豆,苏联的教训不能忘,苏联,老美,大豆,美国,蔬菜,果菜类,新型农...

Demon财经 02-24 21:10:59

今天最新消息 中国国家博物馆推出“罗伯昭捐赠展”亮出珍稀古钱币家底

中新网北京6月29日电(记者应妮)在中国国家博物馆建馆110周年之际,国博特别策划的“薪火赓续——罗伯昭...

互联网 02-24 19:06:26

每日快讯!铝粉爆炸

1、2014年8月2日7时34分,位于江苏省苏州市昆山市昆山经济技术开发区的昆山中荣金属制品有限公司抛光二...

互联网 02-24 17:06:11

【走进区域看发展】加“数”奔跑 九龙坡抢占数字经济发展新高地

数字大屏实时显示了智慧工厂生产线上的情况。新华网刘文静摄新华网重庆2月23日电(李华曾刘文静)智慧工...

新华网 02-24 17:00:49

当前通讯!PiBoy Mini 作为基于 Raspberry Pi 的新型游戏手持设备推出 具有 HDMI 输出

ExperimentalPi已开始销售PiBoyMini,这是一款复古风格的游戏手持设备。根据制造商的说法,Pi

互联网 02-24 14:50:41

天天快播:焦作云台山60岁以上老人游客需要买门票吗?

焦作云台山60岁以上老人游客需要买门票吗?无需买门票。免门票,仅需购买60元 人交通费。(60周岁以上(...

本地宝 02-24 12:04:35

配音演员严明于2月23日去世 享年53岁

严明新浪娱乐讯2月23日,据87级话剧班治丧委员会消息,配音演员严明,原南京市话剧团演员,因病于2023年...

新浪娱乐 02-24 10:17:18

诺思兰德:2月23日获融资买入19.84万元

同花顺数据中心显示,诺思兰德2月23日获融资买入19 84万元,融资偿还0元,当前融资余额33 10万元,占...

同花顺iNews 02-24 08:15:20

天天观热点:怎么屏蔽广告短信_苹果手机怎么屏蔽广告短信

1、短信是一种现在不常用的沟通方式,但并不意味着重要性降低。我们通常会阅读每条短信。如果信息价值小...

元宇宙网 02-24 05:00:16

环球微头条丨异地恋「接吻神器」爆火,「车速」超标!

这两天,一款名为「异地恋接吻神器」的东西火了!顾名思义,这款产品专为异地恋情侣研发,主要是为了解...

果粉俱乐部 02-24 00:13:25

睡不好怎么办?东方医院科普活动教你“先睡心,后睡眠”_全球观天下

本文转自:上观新闻摘要:生活节奏快的今天,不少白领因“睡不着”“睡不好”而备受困扰。人一生中约有...

每日看点快看 02-23 20:52:38

(体育)足球——欧冠:国际米兰胜波尔图

当日,在意大利米兰举行的2022-2023赛季欧洲足球冠军联赛八分之一决赛首回合比赛中,意甲国际米兰队主场...

新华体育 02-23 18:05:48

环球百事通!山西一煤老板,请客用15块“茅台”,13年竟无人看破,秘诀何在?

全国各地的人民都知道,山西的特色就是煤矿多,所以当地也有不少的煤老板,都十分的有钱,而且很多人都...

娟子美食 02-23 16:17:50

宝妈创业月入过万,共享童车为其助力

当前,越来越多的宝妈为了兼顾家庭,放弃了自己的事业。但是有了孩子,各种花销扑面而来,默默计算一下...

哔哩哔哩 02-23 13:51:39

全球视讯!养猪不如养上海人这话传遍旅游圈详细内容

大家好,小太来为大家解答以上问题。养猪不如养上海人!这话传遍旅游圈很多人还不知道,现在让我们一起...

互联网 02-23 12:01:17

米卢:纵观我们国家男足历史,能称得上亚洲顶级的球员,只有三人|全球新消息

范志毅、孙继海、郑智和范志毅同时担任亚洲地区的首席中卫;孙继海同时担任亚洲区首席边后卫;郑智通是...

土豆妈妈 02-23 10:05:38

重点聚焦!蒙古国现状如何?为何经济一直搞不上去?

如下解答:蒙古国发展比较落后的国家。土地面积高达156万平方公里,是全球第2大内陆国家,人口只有320万G...

互联网 02-23 09:15:26

烟台新型冠状病毒肺炎疫情:2月23日烟台疫情最新消息今天数据统计情况通报

烟台新型冠状病毒肺炎疫情:2月23日烟台疫情最新消息今天数据统计情况通报,截至2月23日07时30分烟台疫情...

互联网 02-23 07:52:04

全球速读:端午节的作文400字优秀四年级_端午节的作文400字优秀

1、说到端午节,大家一定会想到香香的粽子。端午节在中国有几千年的历史。农历五月初五,大江南北,长城...

互联网 02-23 03:13:57

今日热搜:4s店喷漆和原厂漆区别有哪些_4s店喷漆和原厂漆区别

1、有区别,一个是机器人喷漆,一个是人喷漆当然不一样了。2、烤漆房和油漆、亮油的成分和比例也不一样...

互联网 02-22 22:37:53

男子屡靠万能钥匙偷车,民警巧用定位装置擒贼

男子屡靠万能钥匙偷车,民警巧用定位装置擒贼---“来,演示一下你是怎么偷电动车的。”面对民警的质问,...

极目新闻 02-22 19:42:59

全球热门:百度2022年财报:全年营收1236.75亿元 拟将多项主流业务与文心一言整合

百度发布2022年四季度及全年未经审计的财报。去年,百度实现营收1236 75亿元,归属百度的净利润206 8...

券中社 02-22 17:32:47

老黄瓜的家常做法大全_老黄瓜的家常做法_观焦点

1、老黄瓜炒土豆片老黄瓜半条,土豆一个,蒜四瓣,干红椒四、五个,花椒粒20个,盐、醋、鸡粉适量。2、1...

互联网 02-22 15:54:52

2022年01月06日整理发布:520表白浪漫的句子 快讯

50个情话是什么?520那天,我们可以在朋友圈、社交平台等发一些浪漫的话。并向我们喜欢的人表达我们的爱...

元宇宙网 02-22 13:38:47

观点:属鼠的和属虎的相克吗_属鼠的和属虎的配吗

1、属鼠和属虎相配吗属鼠的属相婚配表(2008)大吉婚配:龙(辰)、猴(申)、牛(丑)忌婚配:马(午)...

互联网 02-22 11:21:05

天天最资讯丨移动加权平均法公式初级_移动加权平均法公式

1、加权平均法和移动加权平均法区别为:计算对象不同、计算时间不同、及时性不同。2、一、计算对象不同...

互联网 02-22 09:55:00

全球简讯:程门雪医案

1、《程门雪医案》介绍了江南名医程门雪从医五十多年的验案168则及程老学术思想的主要内容。2、所选病案...

互联网 02-22 07:50:53

大厨培训民俗户 教授特色“咬春宴”

原标题:大厨培训民俗户教授特色“咬春宴”摄影 本报记者魏彤21日是农历二月初二,“龙抬头”的日子。...

北京青年报 02-22 03:15:53

世界快报:猪品种大全及图片名字_猪品种大全及图片

1、美国土生土长的杜洛克猪,全身都是棕红色的毛。长得快,5个月能长到180斤。2、苏猪,前苏联改良品种...

元宇宙网 02-21 22:47:22

房产资讯:VivoodLandscapeHotels是由建筑师DanielMayo和一群建筑师与工程师共同开发的新商业模式

这家酒店位于西班牙阿利坎特市附近的酒店,客人呆在预制的小屋中,这些小屋浮在地面上,以减少对崎lands...

互联网 02-21 19:58:24

【天天聚看点】Mysteel日报:全国热轧板卷价格上涨 预计近期价格震荡偏强运行

一、市场总结  21日热轧板卷全国主要城市价格小幅上涨,黑色商品期货高位震荡,05合约收涨1 39%,现...

同花顺财经 02-21 18:00:23

焦点讯息:外墙漆施工工艺剖面_外墙漆施工工艺

1、1施工准备①底材要求:基层抹灰已完成并保养了20天左右,基层的碱度PH值在9以下,同时基层已干燥(至...

互联网 02-21 16:21:58

新疆省造老银元价格(2023年02月21日)

金投白银网提供新疆省造老银元价格(2023年02月21日),新疆银元最新消息(2023年02月21日)。

金投白银网 02-21 14:51:48

全球最新:科学防控 保护孩子的美好“视”界

科学防控保护孩子的美好“视”界

新华社新闻 02-21 12:41:04

红血丝怎么消除最有效_红血丝怎么消除

1、其实红血丝是皮肤科比较难处理的问题,要分原因。2、如果是长期应用激素导致的激素依赖性皮炎和红毛...

互联网 02-21 10:43:49

天天快报!2.21—秋末悔城:金价止跌陷震荡,高接抵挡显神威

上周五,金价在1820上下,形成止跌,并且出现较强反抽;周一,同样延续止跌,并且震荡向上,整体,已经...

中金在线财经号 02-21 09:08:18

世界微头条丨糊精糖尿病人能吃吗_糊精

1、1g糊精+50毫升水,用量筒量50毫升水,先将少量水倒入100毫升烧杯中。2、称1g糊精到烧杯中,搅匀后,...

互联网 02-21 06:09:21

【天天播资讯】深圳哪个医院看皮肤科比较好_深圳北大医院看皮肤科好吗

1、★深圳皮肤病防治所★不错的皮肤病专科医院!现在已经很少能见到医院服务质量这么高的地方了。2、在那...

互联网 02-21 01:12:28

当前视点!百丈漈镇

百丈漈镇位于浙江省温州市文成县西北部,以境内全国最高瀑布“百丈漈”作镇名。东邻大峃镇,南界黄坦镇...

互联网 02-20 21:33:38

中日安全对话和中日外交当局定期磋商将在日本举行

外交部发言人汪文斌20日在例行记者会上证实,经中日双方商定,外交部副部长孙卫东将于2月21日赴日本同日...

新华社 02-20 18:56:59

【聚看点】西红柿炒洋葱的做法_洋葱的做法

1、朋友:你好!洋葱的做法有很多种,这里将做洋葱的两种方法与你分享。2、具体做法如下:洋葱炒鸡蛋:原料:...

元宇宙网 02-20 16:46:04

环球热点!新车 | 售14.48万元起 北汽制造纯电MPV王牌M7 EV上市 2座/5座/7座可选

文:懂车帝原创高帅鹏[懂车帝原创产品]日前,我们从北汽制造获悉,纯电中大型MPV王牌M7EV(图片)正式上市...

懂车帝 02-20 14:54:13

世界热头条丨第一次买车的新手容易入坑?记住以下8点,让你少走冤枉路
【全球速看料】菠萝蜜的吃法_菠萝蜜怎么吃
一个led灯多少流明是什么意思_一个led灯多少流明的意思是什么
如何平整不平的天花板
天天快消息!手脚冰凉怎么办 手脚冰凉如何改善_手脚冰凉怎么办呢
红梅渲染朱樱塔!洛阳重现“朱樱不少见 瀍壑(he)尽阳光”千古美景
钓鱼技巧指南_钓鱼技巧口诀
安庆市第十三届运动会
观速讯丨如何修剪千层树
当前热点-强化复合木地板_强化复合木地板
环球即时:多地宣布:这一假期延长!快看河北人能休多少天→
全球新消息丨关于地球资源的资料_地球资源的资料简介
几种被证明是真的,但你无法接受的理论
热电联产效率92.55%问鼎全球第一!潍柴发布全球首款大功率金属支撑商业化SOFC产品
打通最后100米!贵阳今年将建设30个惠民生鲜小屋
世界快看:画作生动可爱、内容贴近主题,他们荣获“小小规划师”
楼梯井的概念_楼梯井是什么
通讯!买完三星S23系列就退货,国内又被区别对待,售后真该学小米11
信息:婴宁翻译对应_婴宁翻译
焦点!新车品鉴:菲亚特500X Yachting官图发布 配备软顶天幕
播报:警惕!一小区地下室起火,竟是杂物乱堆惹祸?
天天热头条丨《夏花》导演回应争议 称编剧导演都是言承旭粉丝
当前关注:真营养还是真噱头?绿色的大米你会买吗?
赚钱容易吗?选对赛道猪都能飞
全球微动态丨许永生画家简历_许世昌 深圳市书画家协会会员
全球速递!河南理工大学是一本还是二本_河南理工大学地址
坚决整治乡村振兴领域不正之风和腐败问题
近朱者赤近墨者黑相似的句子有哪些_近朱者赤近墨者黑是什么意思
数码宝贝单机版攻略_数码宝贝单机版攻略视频
环球热讯:扬州毅升汽车部件有限公司
环球通讯!【强信心 开好局】同比增长50% 贵州中欧班列实现再增量
淮安水渡口一地块因商业定位,调整规划!
全球视讯!微信也送外卖了? 腾讯回应
【世界时快讯】诺诚健华“黑天鹅”:进医保创新药海外遭抛弃
视焦点讯!什么是红头文件
【世界热闻】长沙居住证办理条件
世界热推荐:移动充值卡怎么充话费
全球头条:温氏股份(300498)2月15日主力资金净买入1789.98万元
当前信息:威尔士赛丁俊晖1个月内2次遭横扫 赛季三度一轮游
交通银行电话9555_95559交通银行网上银行
天天播报:范思哲ins道歉怎样的范思哲辱华事件详情始末介绍
环球新动态:太原市杏花岭区税务局落实“首违不罚”!
姓李以“心”字开头的男孩名字推荐
黄喉是什么_黄喉什么东西
洛克王国格斗小八怎么得 在哪得_洛克王国格斗小五在哪抓
股票20日均线怎么看
环球新资讯:赛为智能:公司业务未涉及坦克机器人
螃蟹怎么洗干净 如何清洗
云南策划推出100个以上省级重大文旅项目
环球观点:boll指标三条线含义是指什么
x 广告
x 广告

Copyright ©  2015-2022 太平洋纤维网版权所有  备案号:豫ICP备2022016495号-17   联系邮箱:93 96 74 66 9@qq.com