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

dede 网站搬家铜川网络推广

dede 网站搬家,铜川网络推广,商城网站建设公司价格,沧州企业做网站一、对象在jvm的创建过程 检查加载--分配内存--内存空间初始化--设置--对象初始化 1) 检查加载 首先检查这个指令的参数是否能在常量池中定位到一个类的符号引用#xff0c;并且检查类是否已经被加载、解析和初始化过。 虚拟机遇到一条 new 指令时#xf…一、对象在jvm的创建过程 检查加载--分配内存--内存空间初始化--设置--对象初始化 1) 检查加载 首先检查这个指令的参数是否能在常量池中定位到一个类的符号引用并且检查类是否已经被加载、解析和初始化过。 虚拟机遇到一条 new 指令时首先检查是否被类加载器加载。如果没有那必须先执行相应的类加载过程。类加载就是把 class 加载到 JVM 的运行时数据区的过程。 2) 分配内存 接下来虚拟机将为新生对象分配内存。为对象分配空间的任务等同于把一块确定大小的内存从 Java 堆中划分出来。 两种分配内存的方法用哪一种取决于内存的规整行 方式一指针碰撞 如果堆内存是绝对规整的已使用的放一 边未使用的放一边中间放着一个指针作为分界点的指示器那所分配内存就仅仅需要把指针向空闲空间挪动一段与对象大小相等的距离。 方式二空闲列表 如果 Java 堆中的内存并不是规整的已使用的内存和空闲的内存相互交错那就没有办法简单地进行指针碰撞了虚拟机就必须维护一个列表记录上哪些内存块是可用的在分配的时候从列表中找到一块足够大的空间划分给对象实例并更新列表上的记录这种分配方式称为“空闲列表”。cms用因为不带整理功能 选择哪种分配方式由java堆是否规整决定而java堆是否规整又由采用的垃圾收集器是否带有 压缩整理功能 决定。 如果是 Serial、ParNew 等带有压缩的整理的垃圾回收器的话系统采用的是指针碰撞既简单又高效。如果是使用 CMS 这种不带压缩整理的垃圾回收器的话理论上只能采用较复杂的空闲列表。 分配内存并发安全问题 对象创建在虚拟机中是非常频繁的行为即使是仅仅修改一个指针所在的位置在并发情况下也并不是线程安全的可能出现正在给对象A分配内存指针还没来的及修改对象B又同时使用了原来的指针分配内存的情况。解决并发安全问题的有以下两种情况 CAS(失败重试机制)TLAB(本地线程分配缓存) 1) CAS机制乐观锁compare and swap 虚拟机采用CAS配上失败重试的方式保证更新操作的原子性 2) 本地线程分配缓冲Thread Local Allocation BufferTLAB 效率比CAS高不需要资源开销 把内存分配的动作按照线程划分在不同的空间中进行即每个线程在Java堆中预先分配一小块私有内存eden区的1%如果分配对象过大就不用这种方式用CAS这就是本地线程分配缓冲Thread Local Allocation BufferTLAB。JVM 在线程初始化时同时也会申请一块指定大小的内存只给当前线程使用这样每个线程都单独拥有一个 Buffer如果需要分配内存就在自己的 Buffer 上分配这样就不存在竞争的情况可以大大提升分配效率当 Buffer 容量不够的时候再重新从 Eden 区域申请一块继续使用。 TLAB 的目的是在为新对象分配内存空间时让每个 Java 应用线程能在使用自己专属的分配指针来分配空间减少同步开销。 TLAB 只是让每个线程拥有私有的分配指针但底下存对象的内存空间还是给所有线程访问的只是其它线程无法在这个区域分配而已。当一个 TLAB 用满分配指针 top 撞上分配极限 end 了就新申请一个 TLAB。 参数 -XX:UseTLAB 启用TLAB在主流的虚拟机上是开启的 允许在新生代代空间中使用线程本地分配块TLAB。默认情况下启用此选项。要禁用 TLAB请指定-XX:-UseTLAB。 3) 内存空间初始化(零值设置) 注意不是构造方法内存分配完成后虚拟机需要将分配到的内存空间都初始化为零值如int值为0boolean值为false等等。这一步操作保证了对象的实例字段在Java代码中可以不赋初始值就直接使用程序能访问到这些字段的数据类型所对应的零值 4) 设置(对象头) 接下来虚拟机要对对象进行必要的设置设置对象头例如这个对象是哪个类的实例如何才能找到类的元数据信息Java classes在Java hostspot VM内部表示为类元数据即类型指针、对象的哈希码、对象的GC分代年龄信息等。这些信息存放在对象头之中。 5) 对象初始化(构造方法) 在上面工作都完成之后从虚拟机的视角来看一个新的对象已经产生了但从 Java 程序的视角来看对象创建才刚刚开始所有的字段都还为零值。所以一般来说执行 new 指令之后会接着把对象按照程序员的意愿进行初始化(构造方法)这样一个真正可用的对象才算完全产生出来。 二、对象的内存布局 在 HotSpot 虚拟机中对象在内存中存储的布局可以分为 3 块区域 对象头Header实例数据Instance Data对齐填充Padding 1、对象头 包括两部分信息 一部分用于存储对象自身的运行时数据(markword)如哈希码HashCode、GC 分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等。 另外一部分是类型指针即对象指向它的类元数据(InstanceKlass就是class对象)的指针虚拟机通过这个指针来确定这个对象是哪个类的实例。 如果对象是一个 java 数组那么在对象头中还有一块用于记录数组长度的数据。 2、实例数据 实例数据部分是实例化的对象程序需要用到的数据 3、对齐填充 对齐填充并不是必然存在的也没有特别的含义它仅仅起着占位符的作用。 由于 HotSpot VM 的自动内存管理系统要求对对象的大小必须是 8 字节的整数倍。当对象其他数据部分没有对齐时就需要通过对齐填充来补全。 三、对象的访问定位 java程序通过虚拟机栈上的局部变量表的reference数据来操作堆上的具体对象。 reference对象的引用 主流两种方法 句柄直接指针 1使用句柄 堆中将会划分出来一块内存作为句柄池reference中存储的就是对象的句柄地址而句柄中包含了对象实例数据和对象类型数据各自的具体址信息。 使用句柄访问的最大好处就是reference中存储的是稳定的句柄地址在对象被移动垃圾收集时移动对象时非常普遍的行为时只会改变句柄中的实例数据指针而reference本身不需要修改 2使用直接指针访问 reference中存储的是对象在堆中的地址。 使用直接指针访问的最大好处就是速度更快它节省了一次指针定位的时间开销由于对象的访问在java中非常频繁因此这类开销积少成多后也是一项非常可观的执行成本。 hotspot使用的是直接指针访问方式进行对象访问的 Class对象位于堆内存中静态变量位于Class对象的尾部。图中的对象类型数据和class对象是同一个。只不过下面的是按照jvm规范介绍class对象在逻辑上数据方法区实际上保存在堆内存。 四、对象的存活判断 没有任何引用指向的一个对象或者多个对象循环引用就是垃圾。需要进行内存回收。 1、判断对象的存活 引用计数法 无法解决循环引用根可达性分析法 jvm采用的是可达性分析法 可以解决循环引用的问题 (1) 引用计数法 在对象中添加一个引用计数器每当有一个地方引用它计数器就加 1当引用失效时计数器减 1。但是无法解决循环引用的问题 AB BA。 (2) 根可达性分析法 这个算法的基本思路就是通过一系列的称为“GC Roots”的对象作为起始点从这些节点开始向下搜索搜索所走过的路径称为引用链Reference Chain当一个对象到 GC Roots 没有任何引用链相连时则证明此对象是不可用的。 作为 GC Roots 的对象包括下面几种重点是前面 4 种 虚拟机栈栈帧中的局部变量表中引用的对象各个线程调用方法堆栈中使用到的参数、局部变量、临时变量等。方法区中类静态属性引用的对象java 类的引用类型静态变量。方法区中常量引用的对象比如字符串常量池里的引用。本地方法栈中 JNI即一般说的 Native 方法引用的对象。JVM 的内部引用class 对象、异常对象 NullPointException、OutofMemoryError系统类加载器。非重点所有被同步锁(synchronized 关键)持有的对象。非重点JVM 内部的 JMXBean、JVMTI 中注册的回调、本地代码缓存等非重点JVM 实现中的“临时性”对象跨代引用的对象 测试对象的回收 // -XX:PrintGC 打印日志观察 public class IsAlive {public Object instance null;//占据内存便于判断分析GCprivate byte[] bigSize new byte[10*1024*1024];public static void main(String[] args) {//objectA 局部变量表 GCRootsIsAlive objectA new IsAlive();//objectB 局部变量表IsAlive objectB new IsAlive();//相互引用 //强引用 //循环引用objectA.instance objectB;objectB.instance objectA;//切断可达objectA null;objectB null;//强制垃圾回收System.gc();} } 打印回收日志 可以发现System.gc()之前是用了25Mgc后用了746k说明上面的循环引用被回收了即当前的jvm用的是根可达分析法 2、class对象的回收条件 注意 Class对象要被回收条件比较苛刻必须同时满足以下的条件仅仅是可以不代表必然因为还有一些参数可以进行控制 1、该类所有的实例都已经被回收也就是堆中不存在该类的任何实例。 2、加载该类的 ClassLoader 已经被回收。 3、该类对应的 java.lang.Class 对象没有在任何地方被引用无法在任何地方通过反射访问该类的方法。 4、参数控制 废弃的常量和静态变量的回收其实就和 Class 回收的条件差不多。 3、Finalize方法 即使通过可达性分析判断不可达的对象也不是“非死不可”它还会处于“缓刑”阶段真正要宣告一个对象死亡需要经过两次标记过程一次是没有找到与 GCRoots 的引用链它将被第一次标记。随后进行一次筛选如果对象重写了 finalize我们可以在 finalize 中去拯救。但是对象只可以被拯救一次(finalize 执行第一次但是不会执行第二次)而且Finalize方法的优先级很低。有可能还没有拯救垃圾回收器就进行第二次筛选了 public class FinalizeGc {public static FinalizeGc instance null;public void isAlive() {System.out.println(I am still alive);}Overrideprotected void finalize() throws Throwable {super.finalize();System.out.println(finalize method executed);FinalizeGc.instance this;}public static void main(String[] args) throws Throwable {instance new FinalizeGc();// 对象进行第1次GCinstance null;System.gc();Thread.sleep(1000);//Finalizer方法优先级很低, 需要等待if (instance ! null) {instance.isAlive();}else {System.out.println(I am dead!);}// 对象进行第2次GCinstance.isAlive();instance null;System.gc();Thread.sleep(1000);if (instance ! null) {instance.isAlive();}else {System.out.println(I am dead!);}} } 结果 finalize method executed I am still alive! I am dead 注释掉两个 sleep public class FinalizeGc {public static FinalizeGc instance null;public void isAlive() {System.out.println(I am still alive);}Overrideprotected void finalize() throws Throwable {super.finalize();System.out.println(finalize method executed);FinalizeGc.instance this;}public static void main(String[] args) throws Throwable {instance new FinalizeGc();// 对象进行第1次GCinstance null;System.gc();// Thread.sleep(1000);//Finalizer方法优先级很低, 需要等待if (instance ! null) {instance.isAlive();}else {System.out.println(I am dead!);}// 对象进行第2次GCinstance.isAlive();instance null;System.gc();// Thread.sleep(1000);if (instance ! null) {instance.isAlive();}else {System.out.println(I am dead!);}} } 运行结果 I am dead finalize method executed I am dead 对象没有被拯救这个就是 finalize 方法执行缓慢还没有完成拯救垃圾回收器就已经回收掉了。所以建议大家尽量不要使用 finalize因为这个方法太不可靠。在生产中你很难控制方法的执行或者对象的调用顺序建议大家忘了 finalize 方法因为在finalize 方法能做的工作java 中有更好的比如 try-finally 或者其他方式可以做得更好 五、对象的各种引用介绍 1.强引用 一般的Object obj new Object()就属于强引用。在任何情况下只有有强引用的关联与根可达还在垃圾回收器就永远不会回收掉被引用的对象。 2.软引用 SoftReference 一些有用但是并非必须用软引用关联的对象系统将要发生内存溢出oom之前这些对象可能就会被回收如过这些对象回收后还是没有足够的空间才会抛出内存溢出多用于缓存 public static void main(String[] args) {// new是强引用User u new User(1, King);// 软引用SoftReferenceUser userSoft new SoftReferenceUser(u);// 干掉强引用确保这个实例只有userSoft的软引用u null;// 这个对象是否还在System.out.println(userSoft.get());// 进行一次GC垃圾回收 千万不要写在业务代码中。System.gc();System.out.println(After gc);System.out.println(userSoft.get());// 往堆中填充数据导致OOMListbyte[] list new LinkedList();try {for (int i 0; i 100; i) {System.out.println(*************userSoft.get());// 1M的对象 100mlist.add(new byte[1024 * 1024 * 1]);}} catch (Throwable e) {// 抛出了OOM异常时打印软引用对象System.out.println(Exception************* userSoft.get());} } 结果 例如一个程序用来处理用户提供的图片。如果将所有图片读入内存这样虽然可以很快的打开图片但内存空间使用巨大一些使用较少的图片浪费内存空间需要手动从内存中移除。如果每次打开图片都从磁盘文件中读取到内存再显示出来虽然内存占用较少但一些经常使用的图片每次打开都要访问磁盘代价巨大。这个时候就可以用软引用构建缓存。 3.弱引用 WeakReference 一些有用但是并非必须用弱引用关联的对象程度比软引用更低。只能生存到下一次垃圾回收之前GC发生时不管内存够不够都会被回收。 public static void main(String[] args) {User u new User(1,King);WeakReferenceUser userWeak new WeakReferenceUser(u);//干掉强引用确保这个实例只有userWeak的弱引用u null;System.out.println(userWeak.get());//进行一次GC垃圾回收,千万不要写在业务代码中。System.gc();System.out.println(After gc);System.out.println(userWeak.get()); } 结果 User [id1, nameKing] After gc null 注意软引用 SoftReference 和弱引用 WeakReference可以用在内存资源紧张的情况下以及创建不是很重要的数据缓存。当系统内存不足的时候缓存中的内容是可以被释放的。 实际运用WeakHashMap、ThreadLocal 案例参考关于Java中的WeakReference - 简书 4.虚引用 PhantomReference 幽灵引用最弱随时会被回收掉垃圾回收的时候收到一个通知就是为了监控垃圾回收器是否正常工作 六、对象的分配原则 Minor Gc 触发条件 Eden满了 当年轻代空间不足时就会触发Minor GC这里的年轻代满指的是Eden代满Survivor满不会引发GC。每次 Minor GC 会清理年轻代的内存。 Survivor满会进行全体新生代晋级 Old 区 Major Gc 触发条件 目前只有CMS GC会有单独收集老年代的行为。 注意很多时候Major GC会和Full GC混淆使用需要具体分辨是老年代回收还是整堆回收。 Full Gc 触发条件 分配担保失败 执行System.gc() 混合收集Mixed GC 收集整个新生代以及部分老年代的垃圾收集 目前只有G1 GC会有这种行为 (1) 栈上分配 为什么能在栈上分配因为逃逸分析在下面 (2) 对象优先在 Eden 区分配 大多数情况下对象在新生代 Eden 区中分配。当 Eden 区没有足够空间分配时虚拟机将发起一次 新生代 GC(Minor GC) 虚拟机参数 -Xms20m 最小堆 -Xmx20m 最大堆 -Xmn10m 新生代大小 -XX:PrintGCDetails 打印垃圾回收日志程序退出时输出当前内存的分配情况 注意新生代初始时就有大小 (3) 大对象直接进入老年代 -Xms20m -Xmx20m -Xmn10m -XX:PrintGCDetails -XX:PretenureSizeThreshold4m 指定大对象的大小 -XX:UseSerialGC 大对象就是指需要大量连续内存空间的 Java 对象最典型的大对象便是那种很长的字符串或者元素数量很庞大的数组。需要连续内存空间的大对象可能提前触发垃圾回收朝生夕死的大对象可能更是频繁触发垃圾回收。我们应注意避免。 这样做的目的1.避免大量内存复制2.避免提前进行垃圾回收新生代比较小 HotSpot 虚拟机提供了-XX:PretenureSizeThreshold 参数指定大于该设置值的对象直接在老年代分配这样做的目的就是避免在 Eden 区及两个 Survivor区之间来回复制产生大量的内存复制操作。 PretenureSizeThreshold 参数只对 Serial 和 ParNew 两款收集器有效。-XX:PretenureSizeThreshold4m。意思是超过这个值的时候对象直接在old区分配内存 (4) 长期存活对象进入老年区 ---新生代垃圾回收 HotSpot 虚拟机中多数收集器都采用了分代收集来管理堆内存那内存回收时就必须能决策哪些存活对象应当放在新生代哪些存活对象放在老年代中。为做到这点虚拟机给每个对象定义了一个对象年龄(Age)计数器存储在对象头中。 如果对象在 Eden 出生并经过第一次 Minor GC 后仍然存活并且能被 Survivor 容纳的话将被移动到 Survivor 空间中并将对象年龄设为 1对象在 Survivor区中每熬过一次 Minor GC年龄就增加 1当它的年龄增加到一定程度(并发的垃圾回收器默认为 15)CMS 是 6 时就会被晋升到老年代中。 -XX:MaxTenuringThreshold 调整进入老年代分代年龄 (5) 对象年龄动态判定 ---新生代垃圾回收 为了能更好地适应不同程序的内存状况虚拟机并不是永远地要求对象的年龄必须达到了 MaxTenuringThreshold 才能晋升老年代如果在 Survivor0/Survivor1(这两只会用一个的) 空间中相同年龄所有对象大小的总和大于 Survivor0/Survivor1 空间的一半年龄大于或等于该年龄的对象就可以直接进入老年代无须等到 MaxTenuringThreshold 中要求的年龄 Minor GC后存活的对象太多无法放入Survivor区了 Minor GC后存活的对象太多导致Survivor区放不下了此时就会将所有的对象直接转移到老年区中 (6) 空间分配担保 ---新生代执行垃圾回收确保老年代内存够。悲观策略。 在发生 Minor GC 之前虚拟机会先检查老年代最大可用的连续空间是否大于新生代所有对象总空间。因为新声代可能全部都是活的。 如果这个条件成立那么 Minor GC 可以确保是安全的。 如果不成立则虚拟机会查看 HandlePromotionFailure 设置值是否允许担保失败。 如果允许那么会继续检查老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小。历次晋升到老年区的均值 与 老年去连续空间对比 如果大于将尝试着进行一次 Minor GC尽管这次 Minor GC 是有风险的如果担保失败则会进行一次 Full GC 如果小于或者 HandlePromotionFailure 设置不允许冒险那这时也要改为进行一次 Full GC 当 Eden 区满了就会触发 Minor GC。清除掉 新生代里的垃圾把 Eden 区剩余存活的对象 复制到 Survivor 0/ Survivor1区 如果同年龄占50%Survivor0大于这个年龄的都放old 区。或者放不下Survivor0就都挪到 old 区 因为 Minor GC 可能需要挪到 old区这时候就需要空间担保。也就可能触发Full GC。Minor GC不是安全的担保失败或者不允许担保就full gc (7) 本地线程分配缓冲 TLAB 七、虚拟机的优化技术 逃逸分析如果对象没有逃逸就在栈上分配内存本地线程分配缓冲(TLAB) 1、逃逸分析 逃逸分析的原理前提是热点代码分析对象动态作用域当一个对象在方法中定义后它可能被外部方法所引用。比如调用参数传递到其他方法中这种称之为方法逃逸。甚至还有可能被外部线程访问到例如赋值给其他线程中访问的变量这个称之为线程逃逸。从不逃逸到方法逃逸到线程逃逸称之为对象由低到高的不同逃逸程度。如果确定一个对象不会逃逸出线程之外那么让对象在栈上分配内存可以提高 JVM 的效率 逃逸分析代码 /**** 逃逸分析-栈上分配* -XX:-DoEscapeAnalysis -XX:PrintGC*/ public class EscapeAnalysisTest {public static void main(String[] args) throws Exception {long start System.currentTimeMillis();//5000万次---5000万个对象for (int i 0; i 50000000; i) {allocate();}System.out.println((System.currentTimeMillis() - start) ms);Thread.sleep(600000);}static void allocate() {//逃逸分析不会逃逸出方法//这个myObject引用没有出去也没有其他方法使用MyObject myObject new MyObject(2020, 2020.6);}static class MyObject {int a;double b;MyObject(int a, double b) {this.a a;this.b b;}} } 这段代码在调用的过程中 Myboject 这个对象属于不可逃逸JVM 可以做栈上分配然后通过开启和关闭 DoEscapeAnalysis 逃逸分析开关观察不同。 JVM 默认开启开启逃逸分析-XXDoEscapeAnalysis 查看执行速度 关闭逃逸分析  查看执行速度 原因 如果是逃逸分析出来的对象可以在栈上分配的话那么该对象的生命周期就跟随线程了就不需要垃圾回收如果是频繁的调用此方法则可以得到很大的性能提高。采用了逃逸分析后满足逃逸的对象在栈上分配没有开启逃逸分析对象都在堆上分配会频繁触发垃圾回收垃圾回收会影响系统性能导致代码运行慢。 代码验证 开启 GC 打印日志 -XX:PrintGC 开启逃逸分析 可以看到没有 GC 日志 关闭逃逸分析 可以看到关闭了逃逸分析JVM 在频繁的进行垃圾回收GC正是这一块的操作导致性能有较大的差别。 2、本地线程分配缓冲(TLAB) TLAB
http://www.dnsts.com.cn/news/186987.html

相关文章:

  • 让做网站策划没经验怎么办品牌买购网
  • 十大接单网站wordpress中注册功能
  • 渭南网站建设电话广告ppt作品
  • 福州建站网络公司知识产权网站模板
  • 广州市官网网站建设平台企业网站关键词排名 s
  • 湖南餐饮网站建设做视频资源网站有哪些内容
  • 衡阳市做淘宝网站建设本地电商平台有哪些
  • 北京网站制作公司清远织梦网站内容怎么做付费可见
  • 杭州模板网站建站成都新站软件快速排名
  • 备案个人网站名称大全网店系统源码
  • 用wordpress仿a站工程承包合作协议书
  • 网站的ico怎么做360外链
  • 设计网站注意哪些问题最新永久ae88v最新人口
  • 广东圆心科技网站开发需要多少钱网件路由器恢复出厂设置
  • 个人备案之后用于商城网站佛山网页设计多少钱
  • 长沙建站工作室专注网站开发
  • 网站建设与开发试卷ui设计发展前景及未来
  • 河南住房与建设厅网站翻译wordpress
  • 网站推广方案注意事项?wordpress 批量打印文章
  • 怎么给一个网站做搜索功能wordpress调用header
  • 自助建设手机网站北京学做网站
  • 传媒公司创业手机网站seo教程下载
  • 网站开发详细设计文档pconline太平洋电脑网
  • 网站建设目的舒城县重点工程建设局网站
  • 网站挂标 怎么做女排联赛排名
  • 服务器做php网站wordpress栏目设置到导航
  • 什么是部署php网站淘宝数据分析
  • 成都网站优化师地方o2o同城网站源码
  • 网站seo 规范中国十大营销专家
  • 不用备案的网站2017主流网站风格