首页 文章 精选 留言 我的

精选列表

搜索[jvm],共6415篇文章
优秀的个人博客,低调大师

JVM内存区域

程序计数器: 当前线程执行行的行号指示器,Java虚拟机多线程通过轮流切换并分配处理器执行时间,一个处理器(对于多和处理器来说是一个内核)都只会一条线程中的指令,为了线程切换后能恢复到正确的执行位置,每条线程都需要一条独立的程序计数器,线程之间计数器互不引响,独立存储,称为线程私有内存。虚拟机规范中唯一没有规定OOM. 虚拟机栈: 线程私有,为虚拟机执行java方法(字节码)服务生命周期与线程相同,虚拟机栈描述的是java方法执行的内存模块:每个方法执行同时会创建一个栈帧用于存储局部变量表、操作数栈、动态链接、方法出口等,每一个方法从调用到执行完成过程,对应着一个栈帧在虚拟机栈中入栈到出栈的过程,规定两种异常:线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError异常,如果虚拟机可动态扩展(当前大部分java虚拟机都可动态扩展,只不过java虚拟机规范中也允许固定长度的虚拟机栈),如果扩展时无法申请到足够内存时,就会抛出OOM. 本地方法栈: 与虚拟机栈发挥的作用非常相似,为虚拟机使用到的Native方法服务,虚拟机规范中对本地方法栈使用的语言、方式与数据结构没有强制规定,Sun HostSpot虚拟机把虚拟机栈和本地方法栈合二为一,本地方法栈区域也会报StackOverflowError和OOM异常 Java堆: java堆是所有线程共享的一块内存区域,在虚拟机启动时创建,此内存唯一目的就是存放实例对象,几乎所有的实例对象及数组都要在推上分配内存,对于大多数应用来说java堆是虚拟机中管理内存最大的一块,java堆是垃圾收集器管理的主要区域,也被称为GC堆,从内存回收的角度看,由于现代收集器都采用分代收机算法,所以堆中还可以细分为:新生代和老年代,再细致一点有Eden空间、From Survivor空间、To Survivor空间,从内存分配的角度来看,线程共享的java堆中可能划分出多个线程私有的分配缓冲区,不过无论怎么划分都与存放内容无关,存放的仍然都是对象实例,进一步划分的目的是为了更好的回收内存,或者更快的分配内存,内存大小既可以固定也可以扩展,可通过Xmx和Xms控制,如果在堆中没有内存完成实例分配、也无法再扩展将会报出OOM异常。 方法区: 方法区和java堆一样,是所有线程共享的内存区域,它用于存储被虚拟机加载的类信息、常类、静态变量、即时编译器编译后的代码等数据,在虚拟机规范中方法区被描述为堆的一个逻辑部分,它有一个别名叫非对,对于习惯在HotSpot虚拟机上开发、部署的开发者来说,很多人称方法区为“永久代(permanent Generation)”本质上两者不等价,永久代有-XX:MaxPermSize上限容易造成内存溢出,方法区无法满足内存分配需求时,将抛出OOM异常。 运行时常量池: 运行时常量池是方法区的一部分,class文件中除了有类的版本、字段、方法、接口等,还有一项常量池(Constant Pool Table)用于存放编译器生成的各种字面量和符号引用,这部分内容将在类加载后进入方法区的运行时常量池存放,运行时常量池受到方法区内存的限制,当常量池无法申请到内存时将抛出OOM异常。 直接内存: 不在运行时数据区内,不受Java堆内存限制,会受到本机总内存限制,同样会抛出OOM异常。

优秀的个人博客,低调大师

JVM垃圾收集

本博客为《深入理解java虚拟机》的学习笔记,所以大部分内容来自此书。 一是否可以回收? Java中对象能否被回收取决于对象是“死”还是“活”,下面我们就介绍一下判定算法。 1引用计数 1) 算法 给对象中添加一个引用计数器,每当有一个地方引用它,计数器加一;当引用失效时,计数器减一;计数器的值为0时,对象就是不能被使用的了,即处于可以被回收的状态了。 2) 分析 这种算法的优点就是简单,但java中无法使用这种方法,因为java中允许对象循环引用,例如Student、Teacher这种模型,我们可以再在Student中引用Teacher,同时在Teacher中引用Student,此时计数器的值无法恢复到0,即无法被回收,这显然与我们的预期不一致。 2可达性分析 java、C#等主流语言的实现中,均是通过可达性分析来判断对象是否存活的。 1) 算法 通过一系列被称之为“GC Root”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称之为引用链(Reference Chain),当一个对象与GC Roots没有引用链相连时,则证明是不可用的。模型如下: 2) GC Root Java中可以作为GC Root对象有以下几种: 虚拟机栈(栈帧中的本地变量表)引用的对象。 方法区中类的静态属性引用的对象。 方法去中常量引用的对象。 本地方法栈中JNI(即一般说的native方法)引用的对象。 3) 引用 JDK1.2之后,java支持以下几种引用:强引用(Strong Reference)、软引用(Soft Reference)、弱引用(Weak Reference)、虚引用(Phantom Reference),他们的强度一次递减。 a) 强引用 只要强引用存在,垃圾收集器就不会回收他们。常用的Object obj = new Object()就属于强引用。 b) 软引用 对于软引用关联的对象,在系统将要发生内存溢出前,将会把这些对象列入回收返回,进行第二次回收。如果这次回收后依然没有足够的内存,才会抛出内存溢出异常。可以通过SoftReference类可以实现软引用。 由此可见,软引用是用来描述还有用但是非必须的对象。 c) 弱引用 使用弱引用关联的对象,只能存活到下一次垃圾收集发生之前,即垃圾收集器工作时,总会收集这部分对象。可以通过WeakReference类来实现弱引用。 由此可见,弱引用也是用来描述还有用但是非必须的对象。 d) 虚引用 虚引用也称幽灵引用或幻影引用,是最弱的一种引用关系。虚引用不会对对象的生命周期产生影响,无法通过虚引用获得一个对象的实例。 对一个对象设置虚引用关联的唯一目的是能够在对象呗垃圾收集器回收时收到一个系统通知。可以通过PhantomReference类来实现虚引用。 3 生死判定 通过可达性分析,在引用链上的对象都不能被回收,但是不在引用链上的对象也不是说一定是“非死不可”的。整个判定过程需要经历两次标记: 1) 第一次标记 如果通过可达性分析后,发现一个对象与GC Root不存在相关联的引用链,那么他将被第一次标记,并且进行一次筛选,即是否有必要执行此对象的finalize()方法。以下两种情况被认为是没有必要执行:对象没有覆盖Object的finalize()方法,或者finalize()方法已经被虚拟机执行过。 如果对象被判定为有必要执行finalize()方法,那么这个对象将会放置在一个叫做F-Queue的队列中,并在稍后由一个虚拟机自动创建、低优先级的Finalizer线程去执行对象的finalize()方法。 finalize()方法时对象逃脱死亡的最后一次机会,如果对象希望拯救自己,那么需要将自己与引用链上的任何一个对象关联起来。例如:将this赋值给某个类变量或者某个对象的成员变量。 注意:虚拟机会触发执行finalize()方法,但是并不会保证等待这个方法执行结束;原因是:如果一个对象在finalize方法中执行缓慢或者死循环,可能会导致F-Queue中其他的对象永久的处于等待,甚至导致整个内存回收崩溃。 2) 第二次标记 GC将会对F-Queue中的对象进行第二次标记,如果对象在finalize()方法中拯救了自己,那么此次标记中会将此对象移除“即将回收”的集合;如果对象在finalize()方法没有拯救自己,那么基本上就认为这个对象已经死了,可以被回收了。 4回收方法区 java虚拟机规范中没有要求在方法区实现垃圾回收。在这一部分进行垃圾回收的性价比一般较低。 永久代的垃圾回收主要包括:废弃常量和无用的类。 1) 回收废弃常量 废弃常量的回收和java堆的的回收非常类似。 以字符串为例,如果常量池中的字符串“abcd”已经没有任何对象引用了,即没有其他地方引用这个“字面量”,如果发生内存回收,那么此“abcd”字符串将会被回收。 常量池中的其他类、接口、方法、字段的符号引用也与之类似。 2) 回收无用的类 类只有满足以下条件才能算作是无用的类。 该类的所有实例已经被回收,即java堆中不存在该类的任何实例。 加载该类的ClassLoader已经被回收。 该类对应的java.lang.Class对象没有在任何地方被引用,无法再任何地方通过反射方位该类的方法。 和堆中对象不同的是,虚拟机可以对无用的类进行回收,但并不是不用了就一定会被回收。 二如何回收? 常见的垃圾收集算法有:标记-清除算法(Mark-Sweep)、复制算法(Coping)、标记-整理算法(Mark-Compact)。 1标记-清除算法 1) 算法 标记-清除分为标记、清除两个阶段。 标记出所有需要回收的对象,在标记完成后统一回收被标记的对象。标记的过程就是前面描述的“生死判定”过程。 2) 不足 Ø 效率不高,标记和清除两个过程效率都不高。 Ø 标记-清除后悔产生大量不连续的内存碎片。 3) 模型 2复制算法 复制算法主要是解决标记清楚算法效率问题而设计的。 1) 算法 将内存按照容量划分成大小相等的两块每次只使用其中的一块,当一块使用完了,将还存活的对象复制到另外一块上,然后将已经使用过的内存空间一次清理掉。 2) 分析 每次对其中的一个块进行内存回收,内存分配时也就不用考虑内存碎片等复杂情况,只需要移动指针到堆顶,然后按顺序分配内存即可。此种方式实现简单、运行高效。 缺点是内存缩小了一半。 3) 模型 4) 改进 a) 改进余地 绝大多数数对象都是朝生夕死的,所以按照1:1的比例来划分内存空间是非常浪费的。一般的做法是:将内存划分成一块较大的Eden空间和两块Survivor空间,每次使用Eden和一块Survivor。当回收时将存活的对象一次性的复制到另外一块Survivor中,然后一次性的清理掉Eden和刚才使用的Survivor空间。 Hotspot虚拟机默认的Eden : Survivor = 8 : 1,即每次使用整个内存空间的90%,浪费10%的空间。 b) 改进模型 c) 新的问题 如果某次gc后发现存活对象的内存大于survivor的大小,将会需要老年代及进行担保。 如果每次gc后存活的对象较多,频繁的复制到另一个survivor中也会导致性能降低。 3标记-整理算法 复制算法的两个问题,导致了其不适合作为老年代的回收算法。 1) 算法 和“标记-清除”算法一样,标记-整理算法也是通过上述“生死判定”过程对对象进行标记,接着将所有存活的对象向一端移动,然后清理掉边界之外的内存。 2) 模型 4分代收集算法 根据对象存活周期的不同,将内存划分为几块,一般将java堆分为新生代、年老代,并根据各个年代的特点采用合适的收集算法。 新生代中,绝大多数对象都是迅速死去,只有少量存活,适合采用优化后的复制算法,只需要付出少量的复制成本就可以完成收集。 年老代因为对象存活率高,并且没有额外的空间对它进行分配担保,所以就必须采用标记-清除、标记-整理算法进行回收。

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册