漫谈 JVM —— 内存
JVM 是什么呢?说的直白点就是 Java 代码运行的地方,全称 Java Virtural Machine,Java 虚拟机。有的人就会奇怪了,为什么 Java 程序员需要了解这个东西?毕竟大多数情况下,“能跑”就行。
能跑真的行吗?你说在一个小公司里,“能跑”就行那是肯定的,业务必定是优先的。可是发展到规模大了之后,“能跑”好像就没那么简单了。规模大了,程序突然崩溃了,却不知道什么情况,这可以叫“能跑”吗?好像不是这样。所以成熟一些公司招程序员,肯定是要懂 JVM 的,不然代码写出了问题,JVM 崩了,都不知道是为什么——打个比方,系统抛出了一个 OOM 了,原因为 null(还真不是 heap),某程序员却不知道为什么 OOM 了,这显然不合适。或许某程序员觉得,这个东西交给运维、或者处在牛 A 和牛 B 之间的人。如果这么想的话,你的解决问题的能力永远无法提升。
那为什么 Java 有虚拟机,C++ 没有呢?因为 C++ 自己管理指针,而 Java 不需要你管理指针—— JVM 帮你管理了。不需要管理指针可以让你的代码更加面向对象(面向对象这几个字,天天被人吹,估计也没几个人懂,有的人竟然吹面向对象而不知道设计模式),让你专注于业务代码。这一切的基础,是你要知道指针是什么。
当然知道指针是什么也是一件复杂的事情。从 C++ 来看,指针可能是 *、& 相关运算符,可是初学的人往往搞不懂设计这些运算符的原因。所以计算机有一门课程叫做汇编,汇编会告诉你这些运算符是怎么来的——和内存地址有关。这就是计算机专业和非计算机专业的不同——计算机专业知道代码时怎么跑的,而非计算机专业不懂得这些,然而这些会限制一个人能不能越走越远。比方说我看《深入理解 Java 虚拟机》难度并不是很高,可能对于不了解指针的人来说,不了解内存的人来说,这本书写的让人感觉云里雾里。
那管理指针有什么用呢?记住了指针,就能记住这块区域存的是什么。执行一段 A 代码,存了一些数据;执行一段 B 代码,存了一些数据;A 代码执行完了,存的那些数据也没有意义了——就是垃圾。垃圾是要回收的,在有限的内存里,垃圾会一直积累,直到占满所有的内存。JVM 帮你做了这些。
那 JVM 是怎么保存这些数据的呢?如果让各个人做可能有不同的方法,现在假设我们有十块内存空间。每一个对象可能占用一块或者多块空间。假设,对象 A 保存第一块,对象 B 保存第二块、第三块。这个时候,程序员小 A 觉得累了,明天再来看这篇文章(看完这段话假设已经过了一天)。第二天,程序员小 A 发现自己记不得那块保存了什么对象、哪个对象占了那几块。这个时候需要一个数据结构去保存对象的位置——也就是对象开始的指针。小 B (就你事情多)这之后问了,我再对象 A 所占块之后空一个区域,表示这个对象结束了,不行吗?那我就想问一句,空对象小 B 你怎么表示呢?小 B 说,空对象用特殊标记啊!那这个特殊标记又怎么特殊标记呢,etc......似乎用空区域表示对象结束不是一个好办法。
所以现在有两个概念——一个是用来存储对象的,一个是用来存储引用的。这两个不同性质的概念应该存在不同的区域。在真实的 Java 虚拟机里,也是这么分的,简单的来说,引用存在 VM Stack 里,实例对象存在 Heap 里。当然,还有一些区域存着别的东西,Native Method Stack(NMS)、Programme Counter Register(PCR)、Method Area(MA)等。你的程序不可能只保存数据,类、方法信息需要保存在 MA 中;你程序执行到哪里了,保存在 PCR 中;Java 有一些代码需要用 C++ 实现,C++ 对象所需要的空间,保存在 NMS 中。
话说回来,我们保存了这些对象,现在需要回收垃圾了。那就涉及到一些问题:
- 回收什么?——垃圾
- 怎么找到所有的垃圾?
- 怎么回收?
怎么找到所有的垃圾?拿得从,程序是怎么跑起来的来看。Java 程序是多线程的,只要跑起来肯定有线程,这些线程的信息被保存到 VM Stack 中。所以什么对象是活跃的?在 VM Stack 中对象肯定是活跃的,这些肯定不是垃圾。还有一些被 static 引用的也不是垃圾,因为很可能下次就会被其他人引用。
听起来似乎简单,实际上并没有那么简单—— VM Stack 里保存了活跃对象 A,A 保存了 B、D,B 保存了 C 等等。这样的结构是树。如何遍历一颗树找到所有的结点那是就是树相关的算法——深度优先搜索或者广度优先搜索了,涉及到数据结构和算法的知识,这里就不多说了。这在 Java 虚拟机中称之为可达性算法。
相对于可达性算法,还有一种算法叫引用计数法。我们维护一个引用列表,A 引用了 B,A 引用次数 + 1;如果没人引用,那肯定是垃圾。似乎这也算起来更简单些,但是小 C 发现了问题:如果有人引用,那它就不是垃圾了吗?其实,存在 A 引用 B, B 引用 A,但是所有运行着的线程都不需要这两个对象,所以这两个也是垃圾。所以引用计数法不是特别靠谱。
说一件事情可能让各位很惊悚,Python 在用引用计数法。引用计数法不会让虚拟机“停下”,可以一边执行一边计算垃圾;而其他的算法,例如标记—清除法,需要实时计算引用,会暂停所有线程去分析。Java 有个著名的问题——Stop The World,STW。在 GC 的时候,服务是不可用的——这对于普通业务来说,还好;对于游戏来说,这是不可接受的,通常这个时间至少达到 100 ms。打个农药突然给你增加延迟 100 ms,你能接受吗?所以,永远没有银弹。
接下来,是怎么回收。想一想现实中垃圾是怎么清楚的,对,现实中。地上有一堆垃圾,丢掉就行了呗。好的,最原始的垃圾清楚方式就是标记—清除。我知道这个地方没用,我就把引用扔掉。这有个问题,比如你有十片地方,空着的地方是 0,没空的地方是 1。原来的分布式是 1111111111,垃圾清楚之后,现在的地方分布式 1010101010。好了,现在来了个大家伙,要占用两个位置,发现没地方放了。果真是没地方放吗?这就和收拾家里一样,挪挪总是有位置摆东西的,我收拾成 1111100000 不就好了,这样我就有两个位置了。这就是标记—清除的缺点——会产生内存碎片。小 D 这时候会说,那我不清除了,我直接把有用的复制到前面来不就好了吗?是啊,这就是标记—整理算法。
然而,90% 的对象都是朝生夕死的,就跟家里的东西一样,大部分都是用完就扔的。这个时候就有人提出了复制算法。具体算法不想多说了,简要来说就是分成两个区域,一个区域保存存活的对象 + 后来产生的对象;另一个区域空下,为下次 GC 做准备,这个区域通常很小,为 10%,正好是存活对象的比例。这样的算法实现起来简单,只要复制到空闲区域,不用担心区域内是不是已经内容、如何移动内容(可能还需要多次移动),而标记—整理算法显然要考虑这些。
JVM 还做了更多的优化,根据对象不同的属性,分成老年代、年轻代,当然算法还不一样。此外还进化出 CMS、G1 算法,这里一句两句讲不清楚,这里就不多提了,为了吞吐量而优化。但一切的一切,有一条重要的原则是 JVM 开发者没有遗忘的——根据实际情况出发。为什么会产生分代?实际情况就是很多对象朝生夕死。
这篇漫谈主要说的是 JVM 内存相关的一些东西,想到什么就写了什么,主要讲了自己的一些感受,具体、准确的分析还请看书、看资料。
漫谈 JVM —— 内存的更多相关文章
- 漫谈 JVM —— 内存模型、线程、锁
Java 内存模型(JMM),实际上的目的就是为了统一内存管理.这让我想到了,作为一个程序员总是想着有银弹,有一个代码能万能的在所有场景上.经过多次尝试我发现这是不可能的:需求在变,技术在更新,没有什 ...
- jvm内存结构及对象漫谈(较全)
最近想整理一下GC相关的知识和经验,在整理之前先整理一下jvm的内存结构,后续会持续更新. jvm内存结构重要由两部分组成:线程共享区域与线程私有区域,如下图所示: 其中方法区和堆为线程共享区域,栈与 ...
- (转)漫谈JVM
漫谈JVM 原文:https://liuzhengyang.github.io/2016/10/05/gossip-jvm/ 背景介绍 JVM已经是Java开发的必备技能了,JVM相当于Java的操作 ...
- Jvm 内存浅析 及 GC个人学习总结
从诞生至今,20多年过去,Java至今仍是使用最为广泛的语言.这仰赖于Java提供的各种技术和特性,让开发人员能优雅的编写高效的程序.今天我们就来说说Java的一项基本但非常重要的技术内存管理 了解C ...
- jvm系列(二):JVM内存结构
JVM内存结构 所有的Java开发人员可能会遇到这样的困惑?我该为堆内存设置多大空间呢?OutOfMemoryError的异常到底涉及到运行时数据的哪块区域?该怎么解决呢?其实如果你经常解决服务器性能 ...
- jvm内存溢出分析
概述 jvm中除了程序计数器,其他的区域都有可能会发生内存溢出 内存溢出是什么? 当程序需要申请内存的时候,由于没有足够的内存,此时就会抛出OutOfMemoryError,这就是内存溢出 内存溢出和 ...
- jvm内存区域
概述 jvm内存分为几个区域: 程序计数器 虚拟机栈 本地方法栈 堆 方法区 运行时常量池 直接内存 这些内存区域是在java进程中细分的,为java程序提供服务 不同的区域存储的内容不一样,生命周期 ...
- JVM内存管理&GC
一.JVM内存划分 |--------------------|-------------PC寄存器-------| |----方法区 ---------|--------------java 虚拟机 ...
- 漫谈JVM
背景介绍 JVM已经是Java开发的必备技能了,JVM相当于Java的操作系统. JVM,java virtual machine, 即Java虚拟机,是运行java class文件的程序. Java ...
随机推荐
- 前端js实现打印(导出)excel表格
产品原型: 图片.png 功能需求:点击导出考勤表格按钮,会自动下载成Excel格式 图片.png 图片.png jsp页面代码: <div class="tools"> ...
- js进阶 11-9/10/11 jquery创建和插入节点
js进阶 11-9/10/11 jquery创建和插入节点 一.总结 一句话总结: 1.jquery插入节点8个方法? 内部之前,内部之后,之前,之后:各两个 append()和appendTo() ...
- PXC安装
安装软件依赖包yum install -y perl-IO-Socket-SSL perl-DBD-MySQL perl-Time-HiRes socat nc openssl-devel l ...
- 【转】A* A星 算法 C语言 实现代码
http://blog.csdn.net/shanshanpt/article/details/8977512 关于A*算法,很早就想写点什么,可是貌似天天在忙活着什么,可事实又没有做什么,真是浮躁啊 ...
- Android 判断软键盘弹出并隐藏的简单完美解决方案
最近项目中有一个编辑框,下面是个ListView.在触发编辑框弹出软键盘后,ListView还能滑动,并且ListView的item还能响应单击.这样的体验效果很不好.于是便想在滑动或单击item时判 ...
- as.data.frame一定要小心的一个參数stringsAsFactors
假设说一个data.frame中的元素是factor.你想转化成numeric,你会怎么做?比方d[1,1]是factor 正确答案是 先as.character(x) 再as.numeric(x ...
- AJAX跨域与JSONP的一点实践经验
前几个周,项目中遇到了AJAX跨域的问题,然后找资料解决了. 首先要说明一点,关于AJAX的跨域原理和实践,我的经验还是比较少的,我只是大致看了下网上的资料,结合自己的理解,找到了解决办法,暂时不去仔 ...
- android 如何创建配置文件和读配置文件
因为一些配置信息,多处用到的.且以后可能变更的,我想写个.prorperties配置文件给管理起来.在studio中新建一个Assets文件-->新建一个file文件类型为properties文 ...
- Linux 在主要的搜索命令和视图的信息
查找命令和硬件信息查看的日常系统管理.最常见的维护操作. 继 Linux 基本查找命令做一个简单的比较.并列出了一些硬件信息经常使用的视图命令. man 经常使用选项 -k 知道功能.不知道名称 -f ...
- 利用WPF建立自己的3d gis软件(非axhost方式)(十一)SDK中的动画系统
原文:利用WPF建立自己的3d gis软件(非axhost方式)(十一)SDK中的动画系统 先下载SDK:https://pan.baidu.com/s/1M9kBS6ouUwLfrt0zV0bPew ...