二、java内存区域与内存溢出异常

0.在内存管理领域,java与c/c++不同的是,在java虚拟机自动内存管理机制下,java不需要手动去为对象写配对的free内存的代码,不容易出现内存泄漏和内存溢出问题。

1.程序计数器:一小块的内存空间,可看作当前线程所执行的字节码的行号指示器。
每条线程都有一个独立的程序计数器,各线程之间计数器互不影响,独立存储,称为“线程私有内存”。
2.java虚拟机栈描述的是java方法执行的内存模型:每个方法执行时都会创建一个栈帧。(Stack Frame)。

虚拟机栈用于存储局部变量表,操作数栈,动态链接,方法出口等。是线程隔离的。

在方法内的变量就叫局部变量,在方法外的变量叫全局变量。
3.本地方法栈,类似虚拟机栈。而本地方法栈,主要用于Native方法服务,也就是JNI相关的。

最常见的JNI是System.currentTimeMills();获取系统时间。
4.java堆:所有线程共享的一块内存模型,在虚拟机启动时创建。
堆用于存放对象实例。堆是垃圾收集器管理的主要区域。如果堆中没有内存可以继续分配,就会抛出OutOfMemoryError。

成员变量(也叫属性变量)是对象实例的一部分。因此成员变量也是存储在jvm堆里面的。
5.方法区:与java堆一样,是各个线程共享的内存区域,用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据

静态域:位于方法区的一块内存。存放类中以static声明的静态成员变量。所以方法区又叫静态区。
常量池:常量池是方法区的一部分内存。常量池在编译期间就将一部分数据存放于该区域,包含基本数据类型如int、long等以final声明的常量值,和String字符串、特别注意的是对于方法运行期位于栈中的局部变量String常量的值可以通过 String.intern()方法将该值置入到常量池中。

7.直接内存:本地直接内存。不受java堆影响。但是内存不够时也会报OOM异常。

8.关于对象的创建,虚拟机遇到new指令时,会先检查这个类是否已被加载、解析、初始化过。如果没有,就执行类加载机制。接下来虚拟机会为新对象分配内存。

虚拟机会对对象进行设置,例如这个对象是哪个类的实例,如何才能找到类的元数据信息、对象的哈希码等。

9.对象的内存布局分为3个区域:对象头、实例数据、对齐填充。

10.对象的访问方式,取决于虚拟机实现而定。对象的访问方式有使用句柄、直接指针两种。使用直接指针访问的好处是速度更快。

Sun HotSpot虚拟机是采用指针访问。

11.OutOfMemory (内存溢出)。

12.堆内存溢出。

解决方法:

  • 调整虚拟机的堆参数(-Xmx 与-Xms)
  • 通过内存映像分析工具,弄清楚是出现了内存泄漏(Memory Leak)还是内存溢出(Memory Overflow)

13.关于虚拟机栈和本地方法栈,在java中描述了两种异常:

  • 如果线程请求的栈深度大于虚拟机所允许的最大深度,将会抛出StackOverflowError异常(栈溢出)
  • 如果虚拟机在扩展栈时无法申请到足够的内存空间,则抛出OutOfMemoryError异常(内存溢出)

14.解决StackOverflowError异常, 提高-Xss参数,增加栈内存容量。

三、垃圾收集器与内存分配策略

1.GC(Garbage Collection,垃圾收集)需要完成的3件事情:哪些内存需要回收?什么时候回收?如何回收?

2.GC发生在java堆和方法区。一个接口中的多个实现类需要的内存不一样,程序处于运行期间才会知道会创建哪些对象,这部分的内存和回收都是动态的。

3.引用计数算法:给对象中添加一个引用计数器,每当有一个地方引用它,计数器值就加一;当引用失效时,计数器值就减一。任何时候计数器为0的对象就是不可能再被使用的。

但是,主流java虚拟机没有选用它来管理内存,因为它很难解决对象之间互相循环引用的问题。

4.可达性分析算法 :从节点开始向下搜索,搜索所走过的路径称为引用链,当一个对象到起点没有任何引用链时,则证明此对象是不可用的。

5.java中的引用分为强引用,软引用,弱引用,虚引用。

6.finalize()是Object的protected方法,子类可以覆盖该方法以实现资源清理工作,GC在回收对象之前调用该方法。

finalize()方法至多由GC执行一次。

7.垃圾收集器:Serial收集器、ParNew收集器、Parallel Scavenge收集器、Serial Old收集器、Parallel Old收集器、CMS收集器、G1收集器

8.  JVM中的堆,一般分为三大部分:新生代、老年代、永久代。

老年代:主要存放应用程序中生命周期长的内存对象。大对象也会直接进入老年代。

新生代:主要是用来存放新生的对象。一般占据堆的1/3空间。由于频繁创建对象,所以新生代会频繁触发MinorGC进行垃圾回收

新生代又分为 Eden区、ServivorFrom、ServivorTo三个区。

  • Eden区:Java新对象的出生地(如果新创建的对象占用内存很大,则直接分配到老年代)。当Eden区内存不够的时候就会触发MinorGC,对新生代区进行一次垃圾回收。
  • ServivorTo:保留了一次MinorGC过程中的幸存者。
  • ServivorFrom:上一次GC的幸存者,作为这一次GC的被扫描者。

MinorGC的过程:MinorGC采用复制算法。

MajorGC采用标记—清除算法:首先扫描一次所有老年代,标记出存活的对象,然后回收没有标记的对象。MajorGC的耗时比较长,因为要扫描再回收。MajorGC会产生内存碎片,为了减少内存损耗,我们一般需要进行合并或者标记出来方便下次直接分配。

当老年代也满了装不下的时候,就会抛出OOM(Out of Memory)异常。

指内存的永久保存区域,主要存放Class和Meta(元数据)的信息,Class在被加载的时候被放入永久区域. 它和和存放实例的区域不同,GC不会在主程序运行期对永久区域进行清理。所以这也导致了永久代的区域会随着加载的Class的增多而胀满,最终抛出OOM异常。

在Java8中,永久代已经被移除,被一个称为“元数据区”(元空间)的区域所取代。

9.垃圾收集算法 :垃圾收集算法介绍

Java有四种类型的垃圾回收器:

  1. 串行垃圾回收器(Serial Garbage Collector)
  2. 并行垃圾回收器(Parallel Garbage Collector)
  3. 并发标记扫描垃圾回收器(CMS Garbage Collector)
  4. G1垃圾回收器(G1 Garbage Collector)

垃圾回收器种类: 垃圾回收器种类

 四、虚拟机性能监控与故障处理工具

1.虚拟机分析数据包括:运行日志、异常堆栈、GC日志、线程快照(threaddump/javacore文件)、堆转储快照(heapdump/hprof文件)等

六、类文件结构

1.由于jvm,java程序可以一次编写,到处运行。

2.jvm具有平台无关性、语言无关性。

3.语言无关性:jvm不和任何语言绑定,只与"class文件"(字节码)这种特定的二进制文件格式所关联。

java,jRuby,Groovy这些语言都可以经过编译器变成字节码(.class文件)

4.Class文件是一组以8位字节为基础单位的二进制流。

Class文件的头4个字节称为魔数。魔数的作用是确定这个文件是否为一个能被虚拟机接受的Class文件。

0xCAFEBABE是Class文件的魔数。这个值在1991年就确定下来。很有浪漫气息~

5.Class文件的第5、6个字节是次版本号。第7、8个字节是主版本号。

6.Class类文件结构包括常量池、访问标志、类索引(父索引、接口索引集合)、字段表集合、方法表集合、属性表集合

7.字节码指令。

七、虚拟机类加载机制

1.虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的java类型,这就是虚拟机的类加载机制。

2.虚拟机类加载机制的生命周期:加载,验证,准备,解析,初始化,使用,卸载。

其中,验证,准备,解析这三个过程又称为“连接”。  

3.加载,验证,准备之后,(解析可能在初始化前也可能在初始化后)。有且只有5种情况必须对类进行“初始化”:

  • 遇到new,getstatic,putstatic,invokestatic这4条字节码指令时,如果类没有初始化,必须进行初始化。
  • 使用reflect包的方法对类进行反射调用时,如果类没有初始化,必须先初始化。
  • 当初始化一个类时,如果发现其父类还没有进行过初始化,需要先触发其父类的初始化。
  • 当虚拟机启动时,用户需要指定一个要执行的main类,虚拟机会先初始化这个主类。
  • 当使用jdk1.7的动态语言支持时,如果一个 java.lang.invoke.MethodHandle实例最后的解析结果REF_getStatic,REF_putStatic,REF_invokeStatic的方法句柄,并且这个方法句柄所对应的类没有进行过初始化,则需要先触发其初始化。

4.加载。加载要完成以下3件事情:

  • 通过一个类的全限定名来获取定义此类的二进制字节流。
  • 将这个字节流所代表的静态存储结构转化为方法区的运行数据结构。
  • 在内存中生成一个代表这个类的Class对象,作为方法区这个类的各种数据的访问入口。

5.验证。验证的目的是为了确保Class文件的字节流中包含的信息符合当前虚拟机的要求,保证安全。

可以从文件格式验证、元数据验证、字节码验证。符号引用验证。

6.准备。 7.解析。

8.初始化。

<clinit>()方法:类构造器方法

<init>()方法:  实例构造器方法  or  类的构造函数

8.1静态语句块中只能访问到定义在静态语句块之前的变量,定义在它之后的变量,在前面的静态语句可以赋值,但是不能访问。

8.2<clinit>()方法与类的构造函数(or 说实例构造器方法<init>()方法)不同。它不需要显示的调用父类构造器,虚拟机会保证在子类的<clinit>()方法执行之前,父类的<clinit>()已经之行完毕。因此在虚拟机中第一个被执行的<clinit>()方法的类肯定是java.lang.Object。
8.3由于父类的<clinit>()方法先执行,也就意味着父类中定义的静态语句块先于子类的变量赋值操作。
8.4、<clinit>()对于类和接口来说,并不是必需的。因为如果一个类中没有静态语句块,也没有对类变量的赋值操作,那么编译器可以不为这个类生成<clinit>()方法。
8.5、接口中不能使用静态语句块,但仍然有变量初始化的赋值操作。因此接口和类一样都会生成<clinit>()。只有当父接口中定义的变量使用是,父接口才会初始化。另外,接口的实现类在初始化时,也一样不会执行接口的<clinit>()方法。
8.6、虚拟机会保证一个类的<clinit>()方法在多线程环境中被正确加锁、同步,如果多个线程同时去初始化一个类,那么只会有一个线程去执行这个类的<clinit>()方法,其他线程都需要阻塞等待,直到活动线程执行<clinit>()方法完毕。如果在一个类中的<clinit>()方法有很耗时的操作,就可能造成多个线程阻塞,在实际应用中,这种阻塞是很隐蔽的。
注:需要注意的是,其他线程虽然会被阻塞,但如果执行<clinit>()方法的那条线程退出<clinit>()方法后,其他线程不会再执行<clinit>()方法。同一个类加载器,一个类型只会初始化一次。

八、类加载器

1.JVM提供了3种类加载器: BootstrapClassLoader、 ExtClassLoader、 AppClassLoader分别加载Java核心类库、扩展类库以及应用的类路径( CLASSPATH)下的类库。JVM通过双亲委派模型进行类的加载,我们也可以通过继承 java.lang.classloader实现自己的类加载器。

2.何为双亲委派模型?当一个类加载器收到类加载任务时,会先交给自己的父加载器去完成,因此最终加载任务都会传递到最顶层的BootstrapClassLoader,只有当父加载器无法完成加载任务时,才会尝试自己来加载。
3.采用双亲委派模型的一个好处是保证使用不同类加载器最终得到的都是同一个对象,这样就可以保证Java 核心库的类型安全,比如,加载位于rt.jar包中的 java.lang.Object类,不管是哪个加载器加载这个类,最终都是委托给顶层的BootstrapClassLoader来加载的,这样就可以保证任何的类加载器最终得到的都是同样一个Object对象。
 
参考资料 :
《深入理解java虚拟机》

《深入理解java虚拟机》笔记的更多相关文章

  1. HTML+CSS笔记 CSS笔记集合

    HTML+CSS笔记 表格,超链接,图片,表单 涉及内容:表格,超链接,图片,表单 HTML+CSS笔记 CSS入门 涉及内容:简介,优势,语法说明,代码注释,CSS样式位置,不同样式优先级,选择器, ...

  2. CSS笔记--选择器

    CSS笔记--选择器 mate的使用 <meta charset="UTF-8"> <title>Document</title> <me ...

  3. HTML+CSS笔记 CSS中级 一些小技巧

    水平居中 行内元素的水平居中 </a></li> <li><a href="#">2</a></li> &l ...

  4. HTML+CSS笔记 CSS中级 颜色&长度值

    颜色值 在网页中的颜色设置是非常重要,有字体颜色(color).背景颜色(background-color).边框颜色(border)等,设置颜色的方法也有很多种: 1.英文命令颜色 语法: p{co ...

  5. HTML+CSS笔记 CSS中级 缩写入门

    盒子模型代码简写 回忆盒模型时外边距(margin).内边距(padding)和边框(border)设置上下左右四个方向的边距是按照顺时针方向设置的:上右下左. 语法: margin:10px 15p ...

  6. HTML+CSS笔记 CSS进阶再续

    CSS的布局模型 清楚了CSS 盒模型的基本概念. 盒模型类型, 我们就可以深入探讨网页布局的基本模型了.布局模型与盒模型一样都是 CSS 最基本. 最核心的概念. 但布局模型是建立在盒模型基础之上, ...

  7. HTML+CSS笔记 CSS进阶续集

    元素分类 在CSS中,html中的标签元素大体被分为三种不同的类型:块状元素.内联元素(又叫行内元素)和内联块状元素. 常用的块状元素有: <div>.<p>.<h1&g ...

  8. HTML+CSS笔记 CSS进阶

    文字排版 字体 我们可以使用css样式为网页中的文字设置字体.字号.颜色等样式属性. 语法: body{font-family:"宋体";} 这里注意不要设置不常用的字体,因为如果 ...

  9. HTML+CSS笔记 CSS入门续集

    继承 CSS的某些样式是具有继承性的,那么什么是继承呢?继承是一种规则,它允许样式不仅应用于某个特定html标签元素,而且应用于其后代(标签). 语法: p{color:red;} <p> ...

  10. HTML+CSS笔记 CSS入门

    简介: </span>年的圣诞节期间,吉多·范罗苏姆为了在阿姆斯特丹打发时间,决心开发一个新的<span>脚本解释程序</span>,作为ABC语言的一种继承. & ...

随机推荐

  1. numpy+pandas 基础学习

    #-*- coding:utf-8 -*- import numpy as np; data1=[1,2,3,4,5] array1=np.array(data1) #创建数组/矩阵 # 使用nump ...

  2. dockerfile 镜像 指定虚拟机的内存

    dockerfile eg: # sea, FROM frolvlad/alpine-oraclejdk8:slim #add volume VOLUME /tmp #add project ADD ...

  3. <记录> Ubuntu16.04 安装Redis以及phpredis扩展

    Linux下安装Redis 1.获取redis资源 wget http://download.redis.io/releases/redis-4.0.8.tar.gz 2.解压 tar xzvf re ...

  4. react-native android 报错 error calling Appregistry.runApplication

    解决了权限问题以为就没问题了,但是进来就红屏了,报错信息如下: 解决了,懒得截图了 error calling Appregistry.runApplication 这个问题也找了很久,开始找到 ht ...

  5. day08-字符串操作

    name = 'hello,world,WORLD! 123,你好' #capitalize()#首字母大写,其他全部变成小写,没有iscapitalize()方法print(name.capital ...

  6. xampp默认mysql数据库root密码的修改

    因为安装xampp后的mysql默认用户root的密码为空,而比如部署Testlink时需要提供数据库密码,此时就需要给root设定密码(网上有些方法,大同小异,但是可能都未标明关键点,未一些出上手的 ...

  7. ABAP-2-会计凭证批量数据导入本地ACCESS

    ABAP-1-会计凭证批量数据导入本地ACCESS 上一版本出现问题: A.若TXT数据条目超过800万(大概1.3G),则将TXT导入ACCESS过程不成功,ACCESS数据表为空.(Access单 ...

  8. SQL 用到的操作符

    1.LIKE 操作符用于在 WHERE 子句中搜索列中的指定模式. SELECT column_name(s) FROM table_name WHERE column_name LIKE patte ...

  9. SQL SERVER 2000安装遇到的问题小汇总(转载)

    [1]安装程序配置服务器失败需要修改下注册表1 打开注册表 在"开始"--"运行"键入 "regedit"  2 删除注册表如下键值: HK ...

  10. jQuery中的end()方法

    定义和用法 end() 方法结束当前链条中的最近的筛选操作,并将匹配元素集还原为之前的状态. 以上是官方说法,比较难理解. 还是用一个例子来说明 <!DOCTYPE html> <h ...