JVM是运行在操作系统之上的,它与硬件没有直接的交互

JVM体系结构

1.类加载器

负责加载class文件,class文件在文件开头有特定的文件标示, 并且ClassLoader只负责class文件的加载,至于它是否可以运行,则由 Execution Engine 执行引擎决定,Execution Engine执行引擎负责解释命令,提 交操作系统执行。

类加载器的分类 (四种)

虚拟机自带的类加载器(三种)

启动类加载器(c++)

扩展类加载器(java)

应用程序加载器(java)

用户自定义加载器()

Java.lang.ClassLoader的子类,用 户可以定制类的加载方式

2.本地方法接口

调用本地系统的接口,用来驱动硬件

3.Native Method Stack(本地方法栈)

它的具体做法是Native Method Stack中登记native方法,在Execution Engine 执行时加载本地方法库。

4.java 栈(实例方法,引用,基本数据类型)

栈 也叫栈内存,主管java程序的运行

是在线程创建时创建,它 的生命期是跟随线程的生命期,线程结束栈内存也就释放,对于栈来说 不存在垃圾回收问题,只要线程一结束该栈就Over,生命周期和线程一 致,是线程私有的。8种基本类型的变量+对象的引用变量+实例方法都 是在函数的栈内存中分配。

栈存储什么?

栈帧中主要保存3 类数据:

本地变量(Local Variables):输入参数和输出参数以及方法内的变量;

栈操作(Operand Stack):记录出栈、入栈的操作;

栈帧数据(Frame Data):包括类文件、方法等等。

栈运行原理:

栈中的数据都是以栈帧(Stack Frame)的格式存在,栈帧是一个内存区 块,是一个数据集,是一个有关方法(Method)和运行期数据的数据集, 当一个方法A被调用时就产生了一个栈帧 F1,并被压入到栈中, A方法又调用了 B方法,于是产生栈帧 F2 也被压入栈, B方法又调用了 C方法,于是产生栈帧 F3 也被压入栈, …… 执行完毕后,先弹出F3栈帧,再弹出F2栈帧,再弹出F1栈帧…… 遵循“先进后出”/“后进先出”原则。

5.程序计数器

每个线程都有一个程序计数器,是线程私有的,就是一个指针, 指向方法区中的方法字节码(用来存储指向下一条指令的地址,也即将 要执行的指令代码),由执行引擎读取下一条指令,是一个非常小的内 存空间,几乎可以忽略不记。

6.方法区

方法区是被所有线程共享,所有字段和方法字节码,以及一些特 殊方法如构造函数,接口代码也在此定义。简单说,所有定义的方法的信 息都保存在该区域,此区属于共享区间。 静态变量+常量+类信息(构造方法/接口定义)+运行时常量池存在方法区中 But 实例变量存在堆内存中,和方法区无关

7.Heap 堆

一个JVM实例只存在一个堆内存,堆内存的大小是可以调节的。 类加载器读取了类文件后,需要把类、方法、常变量放到堆内存中,保 存所有引用类型的真实信息,以方便执行器执行,进一步划分的目的是更好地回收内存,或者更快地分配内存,堆内存分为三部分:

Young Generation Space 新生区 Young/New

Tenure generation space 养老区 Old/ Tenure

Permanent Space 永久区 Perm

图所示的 eden 区、s0 区、s1 区都属于新生代,tentired 区属于老年代。大部分情况,对象都会首先在 Eden 区域分配,在一次新生代垃圾回收后,如果对象还存活,则会进入 s0 或者 s1,并且对象的年龄还会加 1(Eden 区->Survivor 区后对象的初始年龄变为 1),当它的年龄增加到一定程度(默认为 15 岁),就会被晋升到老年代中。对象晋升到老年代的年龄阈值,可以通过参数-XX:MaxTenuringThreshold 来设置。

新生区 新生区是类的诞生、成长、消亡的区域,一个类在这里产生,应用,最后被垃圾 回收器收集,结束生命。新生区又分为两部分: 伊甸区(Eden space)和幸存者区 (Survivor pace) ,所有的类都是在伊甸区被new出来的。幸存区有两个: 0区 (Survivor 0 space)和1区(Survivor 1 space)。当伊甸园的空间用完时,程序又需要 创建对象,JVM的垃圾回收器将对伊甸园区进行垃圾回收(Minor GC),将伊甸园区中的不 再被其他对象所引用的对象进行销毁。然后将伊甸园中的剩余对象移动到幸存 0区。若幸 存 0区也满了,再对该区进行垃圾回收,然后移动到 1 区。那如果1 区也满了呢?再次垃 圾回收,满足条件后(默认15岁)再移动到养老区。若养老区也满了,那么这个时候将产生MajorGC (FullGC),进行养老区的内存清理。若养老区执行了Full GC之后发现依然无法进行对象 的保存,就会产生OOM异常“OutOfMemoryError”。

如果出现java.lang.OutOfMemoryError: Java heap space异常,说明 Java虚拟机的堆内存不够。原因有二:

(1)Java虚拟机的堆内存设置不够,可以通过参数-Xms、-Xmx来调整。

(2)代码中创建了大量大对象,并且长时间不能被垃圾收集器收集(存 在被引用)。

永久区 永久存储区是一个常驻内存区域,用于存放JDK自身所携带的 Class,Interface 的元数据,也就是说它存储的是运行环境必须的类信息,被装 载进此区域的数据是不会被垃圾回收器回收掉的,关闭 JVM 才会释放此区域所 占用的内存。

常量池(Constant Pool)是方法区的一部分,Class文件除了有类的版本、字段、方法、 接口等描述信息外,还有一项信息就是常量池,这部分内容将在类加载后进入方法区的运行时常 量池中存放。

堆数据调优简介

-Xms1024m -Xmx1024m -XX:+PrintGCDetails

方法区和永久代的关系

《Java 虚拟机规范》只是规定了有方法区这么个概念和它的作用,并没有规定如何去实现它。那么,在不同的 JVM 上方法区的实现肯定是不同的了。 方法区和永久代的关系很像 Java 中接口和类的关系,类实现了接口,而永久代就是 HotSpot 虚拟机对虚拟机规范中方法区的一种实现方式。 也就是说,永久代是 HotSpot 的概念,方法区是 Java 虚拟机规范中的定义,是一种规范,而永久代是一种实现,一个是标准一个是实现,其他的虚拟机实现并没有永久带这一说法。

为什么要将永久代 (PermGen) 替换为元空间 (MetaSpace) 呢?

整个永久代有一个 JVM 本身设置固定大小上线,无法进行调整,而元空间使用的是直接内存,受本机可用内存的限制,并且永远不会得到 java.lang.OutOfMemoryError。你可以使用 -XX:MaxMetaspaceSize 标志设置最大元空间大小,默认值为 unlimited,这意味着它只受系统内存的限制。-XX:MetaspaceSize 调整标志定义元空间的初始大小如果未指定此标志,则 Metaspace 将根据运行时的应用程序需求动态地重新调整大小。

当然这只是其中一个原因,还有很多底层的原因,这里就不提了。

String 类和常量池

String 对象的两种创建方式:

String str1 = "abcd";//先检查字符串常量池中有没有"abcd",如果字符串常量池中没有,则创建一个,然后 str1 指向字符串常量池中的对象,如果有,则直接将 str1 指向"abcd""; String str2 = new String("abcd");//堆中创建一个新的对象 String str3 = new String("abcd");//堆中创建一个新的对象 System.out.println(str1==str2);//false System.out.println(str2==str3);//false

第一种方式是在常量池中拿对象;

第二种方式是直接在堆内存空间创建一个新的对象。

String 类型的常量池比较特殊。它的主要使用方法有两种:

直接使用双引号声明出来的 String 对象会直接存储在常量池中。

如果不是用双引号声明的 String 对象,可以使用 String 提供的 intern 方法。String.intern() 是一个 Native 方法,它的作用是:如果运行时常量池中已经包含一个等于此 String 对象内容的字符串,则返回常量池中该字符串的引用;如果没有,则在常量池中创建与此 String 内容相同的字符串,并返回常量池中创建的字符串的引用。

String s1 = new String("计算机"); String s2 = s1.intern(); String s3 = "计算机"; System.out.println(s2);//计算机 System.out.println(s1 == s2);//false,因为一个是堆内存中的 String 对象一个是常量池中的 String 对象, System.out.println(s3 == s2);//true,因为两个都是常量池中的 String 对象

字符串拼接:

String str1 = "str"; String str2 = "ing"; String str3 = "str" + "ing";//常量池中的对象 String str4 = str1 + str2; //在堆上创建的新的对象 String str5 = "string";//常量池中的对象 System.out.println(str3 == str4);//false System.out.println(str3 == str5);//true System.out.println(str4 == str5);//false

String s1 = new String("abc");这句话创建了几个字符串对象?

将创建 1 或 2 个字符串。如果池中已存在字符串文字“abc”,则池中只会创建一个字符串“s1”。如果池中没有字符串文字“abc”,那么它将首先在池中创建,然后在堆空间中创建,因此将创建总共 2 个字符串对象。

验证:

String s1 = new String("abc");// 堆内存的地址值 String s2 = "abc"; System.out.println(s1 == s2);// 输出 false,因为一个是堆内存,一个是常量池的内存,故两者是不同的。 System.out.println(s1.equals(s2));// 输出 true

4.3 8 种基本类型的包装类和常量池

Java 基本类型的包装类的大部分都实现了常量池技术,即 Byte,Short,Integer,Long,Character,Boolean;这 5 种包装类默认创建了数值[-128,127] 的相应类型的缓存数据,但是超出此范围仍然会去创建新的对象。

两种浮点数类型的包装类 Float,Double 并没有实现常量池技术。

Integer i1 = 33; Integer i2 = 33; System.out.println(i1 == i2);// 输出 true Integer i11 = 333; Integer i22 = 333; System.out.println(i11 == i22);// 输出 false Double i3 = 1.2; Double i4 = 1.2; System.out.println(i3 == i4);// 输出 false

栈溢出怎么定义解决:

第一对所有的代码包括页面中的java代码都进行一遍彻底的回顾检查, 1.对那些静态(static)的对象要特别留神,特别是类型为Map,List,Set的,静态的变量会一直驻存在内存中,生命周期比较长,不会被垃圾器回收。 2.对于代码,要审查是否生成了大量的冗余的对象,还有一些逻辑业务处理的类, 算法是否过于复杂,调整算法,对于代码认真审查,再仔细重构一遍代码,能提高代码质量,提高程序运行稳定性。 3.Java中的内存溢出大都是因为栈中的变量太多了。其实内存有的是。建议不用的尽量设成null以便回收,多用局部变量,少用成员变量。 1),变量所包含的对象体积较大,占用内存较多。 2),变量所包含的对象生命周期较长。 3),变量所包含的对象数据稳定。 4),该类的对象实例有对该变量所包含的对象的共享需求。 4.在我的程序中对静态变量的优化后,使程序占用内存量至少提升了5k-10k。所以也不容忽视。 第二还有就是String类相关的东西: 1.字符串累加的时候一定要用StringBuffer的append方法,不要使用+操作符连接两个字符串。差别很大。而且在循环或某些重复执行的动作中不要去创建String对象,因为String对象是要用StringBuffer对象来处理的,一个String对象应该是产生了 3个对象(大概是这样:))。 2.字符串length()方法来取得字符串长度的时候不要把length放到循环中,可以在循环外面对其取值。(包括vector的size方法)。特别是循环次数多的时候,尽量把length放到循环外面。 int size = xmlVector.size(); for (int i = 2; i < size; i++) { 。。。 } 3 写代码的时候处理内存溢出 try{ //do sth .... }catch (outofmemoryerror e){//可以用一个共通函数来执行. system.out.print (“no memory! ”); system.gc(); //do sth again .... } 4.对于频繁申请内存和释放内存的操作,还是自己控制一下比较好,但是System.gc()的方法不一定适用,最好使用finallize强制执行或者写自己的finallize方法。 Java 中并不保证每次调用该方法就一定能够启动垃圾收集,它只不过会向JVM发出这样一个申请,到底是否真正执行垃圾收集,一切都是个未知数。

5.出现了循环递归调用

java一个接口执行结束释放内存_java的灵魂--JVM虚拟机相关推荐

  1. java 及时释放内存_Java 内存释放

     问题一什么叫垃圾回收机制 垃圾回收是一种动态存储管理技术它自动地释放不再被程序引用的对象按照特定的垃圾收集算法来实现资源自动回收的功能.当一个对象不再被引用的时候内存回收它占领的空间 ...

  2. java没有释放内存_java – G1年轻的GC没有释放内存 – 空间耗尽

    我正在使用G1GC,jdk 1.7 Java HotSpot(TM) 64-Bit Server VM (24.79-b02) for linux-amd64 JRE (1.7.0_79-b15), ...

  3. java byte 释放内存_java java.nio.ByteBuffer.allocateDirect 导致内存泄露

    java能够经过java.nio.ByteBuffer.allocateDirect(capacity)直接运用non java heap(java堆外)的内存 . 一.运用意图: 1.拓荒数据缓冲区 ...

  4. java 代码段 执行超时 抛异常_Java 并发队列详解TransferQueue、BlockingQueue、BlockingDeque

    阻塞队列 BlockingQueue java.util.concurrent 包里的 BlockingQueue 接口表示一个线程安放入和提取实例的队列. BlockingQueue 用法 Bloc ...

  5. 出大事了,涛哥你们Java应用GC后不释放内存

    你知道的越多,不知道的就越多,业余的像一棵小草! 成功路上并不拥挤,因为坚持的人不多. 编辑:业余草 blog.csdn.net/qq_40378034 推荐:https://www.xttblog. ...

  6. java定义接口区分飞机和汽车_JAVA菜鸟入门篇 - 抽象类、接口 (十九)

    一.抽象类 抽象类(abstract class):使用了abstract关键字所修饰的类叫做抽象类. 为什么需要抽象类?如何定义抽象类? 是一种模板模式.抽象类为所有子类提供一个通用模板,子类可以再 ...

  7. java 一个接口可以继承多个接口吗

    接口是常量值和方法定义的集合.接口是一种特殊的抽象类. java类是单继承的.classB Extends classA java接口可以多继承.Interface3 Extends Interfac ...

  8. java一个接口可以继承另外一个接口吗

    一个接口可以继承多个接口. interface C extends A, B {}是可以的.一个类可以实现多个接口: class D implements A,B,C{}但是一个类只能继承一个类,不能 ...

  9. java 及时释放内存_Java 等语言的 GC 为什么不实时释放内存?

    最基本的纯引用计数方式的自动内存管理可以做到实时释放死对象,但却无法处理存在循环引用的对象图的释放.这个问题一定程度上可以通过引入弱引用的概念来解决,但通用的能处理带循环引用对象图的引用计数都是有别的 ...

最新文章

  1. 高颜值免费在线SCI绘图工具增加上传功能
  2. C++重载、覆盖和遮蔽
  3. WIFI配置专项测试
  4. 205. jetcache:你需要知道的小技巧
  5. ibatis--百度百科
  6. python判断最小公倍数
  7. ad用户和计算机显示2000个,AD 用户属性userAccountControl的详细解释
  8. UNIX网络编程的5种IO模型
  9. 麦克斯韦电磁场理论基础
  10. OLED显示模块(原理讲解、STM32实例操作)
  11. 让人感到惊艳的5款数据可视化大屏产品
  12. 物联网、大数据和云计算的关系
  13. 关于计算机信息管理专业教学改革的相关文章,计算机信息管理专业论文范文
  14. 为什么逍遥子说,双11已成全球的社会大协同?
  15. 2019 年中国搜索引擎市场份额排行榜
  16. 用笔记本改装智能家居服务器,超级DIY笔记本和台式机改装成平板电脑
  17. python文件批量改名
  18. python main.py是什么意思_什么是__main__.py?
  19. SDN控制器测试专题一:基础知识篇
  20. 让macOS词典具备保存单词的生词本功能

热门文章

  1. 填平新版本Xcode安装插件不成功的坑
  2. LightSpeed 的Left Join Bug解决方案
  3. jQuery Form Plugin (jquery表单插件)
  4. 如何动态调用WebServices
  5. 保护模式及其编程——8086系统寄存器和系统指令
  6. go中NOSQL数据库操作
  7. 后台开发经典书籍--mysql从入门到精通
  8. http 400错误
  9. Arduino 控制舵机
  10. 设计模式:单例模式之枚举