Java虚拟机在执行Java程序的过程当中会把它所管理的内存划分为若干个不一样的数据区域。根据《Java虚拟机规范(Java SE 7版)》的规定,Java虚拟机所管理的内存将会包括如下几个运行时数据区域:java
描述的是Java方法执行的内存模型算法
Java虚拟机规范规定2种异常状况:数组
永久代:HotSpot在1.7以前把GC分代收集扩展至方法区,即用永久代实现方法区缓存
若无,则执行类加载过程安全
同步分配内存空间2种方式:服务器
内存分配完成后,虚拟机须要将分配到的内存空间都初始化为零值(不包括对象头)数据结构
<init>
方法,初始化对象。HotSpot VM中,对象在内存中的布局:多线程
对象头(Header)架构
如下是Java程序经过栈上的Reference来操做堆上的具体对象。并发
方式一:使用句柄
方式二:使用直接指针
HotSpot使用这种
在HotSpot虚拟机中并不区分虚拟机栈和本地方法栈
不考虑虚拟机自己耗费内存、程序计数器内存(很小) 虚拟机栈和本地方法栈分配到的内存 = 进程内存 - 最大堆内存(Xmx)- 最大方法区(MaxPermSize) 因此线程数越多,单个线程内存就越小,成反比
-XX:MaxDirectMemorySize
进行设置,不设置则等同于Heap最大值。本章讨论Heap内存的分配和回收
给对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加1;当引用失效时,计数器值就减1;任什么时候刻计数器为0的对象就是不可能再被使用的。很难解决对象之间相互循环引用的问题
这个算法的基本思路就是经过一系列的称为"GC Roots"的对象做为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链相连(用图论的话来讲,就是从GC Roots到这个对象不可达)时,则证实此对象是不可用的。
可做为CG Root的对象:
Object() obj = new Object();
,只要存在引用,便没法进行垃圾收集因此主动调用finalize()并不能当即触发GC,它不是C++中的析构函数
永久代收集内容:
无用类:
不足:
安全点位置选定还需考虑GC时让全部线程都进入此
在线程执行到Safe Region中的代码时,首先标识本身已经进入了Safe Region,那样,当在这段时间里JVM要发起GC时,就不用管标识本身为Safe Region状态的线程了。在线程要离开Safe Region时,它要检查系统是否已经完成了根节点枚举(或者是整个GC过程),若是完成了,那线程就继续执行,不然它就必须等待直到收到能够安全离开Safe Region的信号为止。
并行(Parallel):指多条垃圾收集线程并行工做,但此时用户线程仍然处于等待状态。并发(Concurrent):指用户线程与垃圾收集线程同时执行(但不必定是并行的,可能会交替执行),用户程序在继续运行,而垃圾收集程序运行于另外一个CPU上。
关注的维度不一样
吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间)虚拟机总共运行了100分钟,其中垃圾收集花掉1分钟,那吞吐量就是99%。
缺点:
浮动垃圾:并发清除时用户线程还在运行,可能在标记过程后产生部分垃圾,只能留到下次GC时清除。
G1的运做步骤:
jps[options][hostid] jps能够经过RMI协议查询开启了RMI服务的远程虚拟机进程状态,hostid为RMI注册表中注册的主机名
jstat[option vmid[interval[s|ms][count]]] interval:查询间隔 count:次数 #每250毫秒查询一次进程2764垃圾收集情况,一共查询20次 jstat -gc 2764 250 20
jinfo[option]pid # 查询CMSInitiatingOccupancyFraction参数值 $ jinfo -flag CMSInitiatingOccupancyFraction 13435 -XX:CMSInitiatingOccupancyFraction=-1
其余方式得到dump文件:
jstack[option]vmid
在大多数网站形式的应用里,主要对象的生存周期都应该是请求级或者页面级的,会话级和全局级的长生命对象相对不多。只要代码写得合理,应当都能实如今超大堆中正常使用而没有Full GC,这样的话,使用超大堆内存时,网站响应速度才会比较有保证。
垃圾收集进行时,虚拟机虽然会对Direct Memory进行回收,可是Direct Memory却不能像新生代、老年代那样,发现空间不足了就通知收集器进行垃圾回收,它只能等待老年代满了后Full GC,而后“顺便地”帮它清理掉内存的废弃对象。不然它只能一直等到抛出内存溢出异常时,先catch掉,再在catch块里面“大喊”一声:"System.gc()!"。要是虚拟机仍是不听(譬如打开了-XX:+DisableExplicitGC开关),那就只能眼睁睁地看着堆中还有许多空闲内存,本身却不得不抛出内存溢出异常了。而本案例中使用的CometD 1.1.1框架,正好有大量的NIO操做须要使用到Direct Memory内存。
从实践经验的角度出发,除了Java堆和永久代以外,咱们注意到下面这些区域还会占用较多的内存,这里全部的内存总和受到操做系统进程最大内存的限制。
Java的Runtime.getRuntime().exec()方法,首先克隆一个和当前虚拟机拥有同样环境变量的进程,再用这个新的进程去执行外部命令,最后再退出这个进程。若是频繁执行这个操做,系统的消耗会很大,不只是CPU,内存负担也很重
虚拟机规范则是严格规定了有且只有5种状况必须当即对类进行“初始化”(而加载、验证、准备天然须要在此以前开始):
验证的4个阶段:
从Java开发人员角度能够大体细分程3种:
启动类加载器(Bootstrap ClassLoader)[不能直接使用]
扩展类加载器(Extension ClassLoader),[可直接使用]
应用程序类加载器(Application ClassLoader),[可直接使用]
protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { // 首先判断该类型是否已经被加载 Class c = findLoadedClass(name); if (c == null) { // 若是没有被加载,就委托给父类加载或者委派给启动类加载器加载 try { if (parent != null) { // 若是存在父类加载器,就委派给父类加载器加载 c = parent.loadClass(name, false); } else { // 若是不存在父类加载器,就检查是不是由启动类加载器加载的类,经过调用本地方法native Class findBootstrapClass(String name) c = findBootstrapClass0(name); } } catch (ClassNotFoundException e) { // 若是父类加载器和启动类加载器都不能完成加载任务,才调用自身的加载功能 c = findClass(name); } } if (resolve) { resolveClass(c); } return c; }
主流Java Web服务器要解决的问题:
Tomcat的目录结构: