热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

java定位内存溢出_Java内存区域与内存溢出

前言最近在读周志明老师的《深入理解Java虚拟机》,感觉一下换了一个角度来看待Java代码,有必要整理一些内容,更清楚实际的流程,这一篇就

前言

最近在读周志明老师的《深入理解Java虚拟机》,感觉一下换了一个角度来看待Java代码,有必要整理一些内容,更清楚实际的流程,这一篇就记录下Java内存区域与相关的一些内存溢出的异常。

内存区域

Java虚拟机在执行Java程序的过程会把它管理的内存划分为各个不同的区域,这些区域都有着各自的生命周期,总的来说Java虚拟机管理的内存将会包括一下的数据区域

b0e75be6fd830384eb6297575defbea4.png

图中可以很清晰的看出区域里面各个实体的关系,然后简单介绍一3下各个实体。

1.程序计数器

线程私有,可以看作是当前线程所执行的字节码的行号指示器,字节码解释器的工作就是通过改变计数器的值来进行后续的操作。

2.虚拟机栈

线程私有,描述的是Java方法执行的内存模型,每个方法在执行的同时会创建一个栈帧用于储存局部变量表、操作数栈、动态链接、方法出口等信息。

3.本地方法栈

与虚拟机栈发挥的作用类似,但虚拟机栈主要是为执行Java方法服务,而本地方法栈则为虚拟机使用到的native方法服务。

4.Java堆

是Java虚拟机所管理的内存中最大的一块,也是被所有线程共享的一块内存区域,在虚拟机启动时创建,此区域唯一目的是存放对象实例。

5.方法区

也是各个线程共享,用于储存已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据,为堆的一个逻辑部分。其中的运行时常量池相对于Class文件常量池而言具备动态性,运行期间也可将新的常量放入池中。

除了这些以外,直接内存的不合理分配也会影响到Java虚拟机动态扩展内存时出现内存溢出。

从JVM看对象

Java对象的创建我们无时无刻都在使用着,这里从虚拟机层面来看待对象的创建。

对象的创建

1.当虚拟机收到一条new指令时,首先去检查这个指令的参数能否在常量池中定位到对应的符号引用,并且检查符号引用代表的类是否加载过、解析和初始化过,没有则进行对应的类加载过程。

2.在类检查通过后,虚拟机为新生对象从Java堆中分配内存。而内存的分配又有两种方式。一种是指针碰撞,就是把空闲和正在使用的内存中间放一个指针隔开,所以这种方式实际就是把指针进行移动,当内存出现相互交错时。该方式自然就行不通了;另一种方式是空闲列表,由虚拟机维护一个列表,分配内存后就更新这个列表的记录。至于使用哪种方式就要看是基于何种垃圾回收器而言。除了怎么划分可用空间之外,还需要考虑虚拟机分配内存的频率,分配频率就会涉及到并发中的一些问题了。JVM采用CAS(比较并交换)方式进行失败重试保证操作的原子性;另一种是每个线程在Java堆中预先分配一小块内存,也就是本地线程分配缓冲(TLAB)

3.内存分配完成后,虚拟机将分配到的内存空间都初始化为零值,使用TLAB,则可以提前到分配时进行。

4.虚拟机对对象进行必要的设置,也就是把该对象相关的信息存储在对象头之中,这些工作都完成后,一个新的对象已经产生了,接下来就是对象的初始化了。

对象的访问定位

对象的访问目前主流的方式有句柄和直接指针两种。

1.使用句柄访问,Java堆会划分出一块内存作为句柄池,对象的引用中存储的就是其句柄的地址,句柄包含了对象实例数据和类型数据的具体地址信息。

2.使用直接指针,堆就要考虑如何放置访问类型数据相关的信息,引用中存储的直接就是对象地址。

很显然,因为是直接指向地址,所以直接指针的方式更加快,但对象访问在Java中太过频繁,积少成多后也会造成较高的执行成本。

内存溢出异常

1.Java堆溢出

public class HeapOOM {

static class OOMObject {

}

public static void main(String[] args) {

List list &#61; new ArrayList<>();

while (true) {

list.add(new OOMObject());

}

}

}

这里可以设置下虚拟机相关的参数,

-verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:&#43;PrintGCDetails -XX:SurvivorRatio&#61;8

2.虚拟机和本地方法栈溢出

public class JavaVMStackSOF {

private int stackLength &#61; 1;

public void stackLeak() {

stackLength&#43;&#43;;

stackLeak();

}

public static void main(String[] args) {

JavaVMStackSOF stackSOF &#61; new JavaVMStackSOF();

try {

stackSOF.stackLeak();

} catch (Throwable e) {

System.out.println("Stack length: "&#43;stackSOF.stackLength );

throw e;

}

}

}

3.创建线程导致的内存溢出

public class JavaVMStackOOM {

private void doStop() {

while (true) {

}

}

public void stackLeakByThread() {

while (true) {

Thread thread &#61; new Thread(()->{

doStop();

});

thread.start();

}

}

public static void main(String[] args) {

JavaVMStackOOM oom &#61; new JavaVMStackOOM();

oom.stackLeakByThread();

}

}

这里的话,在Windows平台的虚拟机中&#xff0c;Java的线程是映射到操作系统的内核线程上&#xff0c;这里可能导致机器假死。

4.方法和运行时常量池溢出

public class RuntimeConstantPoolOOM {

public static void main(String[] args) {

List list &#61; new ArrayList<>();

int i &#61; 0;

while (true) {

list.add(String.valueOf(i&#43;&#43;).intern());

}

}

}

5.本机直接内存溢出

public class DirectMemoryOOM {

public static final int _1MB &#61; 1024 * 1024;

public static void main(String[] args) {

Field unsafeField &#61; Unsafe.class.getDeclaredFields()[0];

unsafeField.setAccessible(true);

try {

Unsafe unsafe &#61; (Unsafe) unsafeField.get(null);

while (true) {

unsafe.allocateMemory(_1MB);

}

} catch (IllegalAccessException e) {

e.printStackTrace();

}

}

}

总结

了解了这些不得不佩服设计者&#xff0c;一个看似简单的东西底层要解决的问题、处理的细节也是非常多的&#xff0c;从Java虚拟机层面看到了不一样的东西&#xff0c;也是非常有意思的&#xff0c;理解底层实现的原理有助于写成更健壮的代码&#xff0c;更快速的debug&#xff0c;就到这里了。



推荐阅读
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • Java学习笔记之面向对象编程(OOP)
    本文介绍了Java学习笔记中的面向对象编程(OOP)内容,包括OOP的三大特性(封装、继承、多态)和五大原则(单一职责原则、开放封闭原则、里式替换原则、依赖倒置原则)。通过学习OOP,可以提高代码复用性、拓展性和安全性。 ... [详细]
  • Android系统源码分析Zygote和SystemServer启动过程详解
    本文详细解析了Android系统源码中Zygote和SystemServer的启动过程。首先介绍了系统framework层启动的内容,帮助理解四大组件的启动和管理过程。接着介绍了AMS、PMS等系统服务的作用和调用方式。然后详细分析了Zygote的启动过程,解释了Zygote在Android启动过程中的决定作用。最后通过时序图展示了整个过程。 ... [详细]
  • 本文整理了Java面试中常见的问题及相关概念的解析,包括HashMap中为什么重写equals还要重写hashcode、map的分类和常见情况、final关键字的用法、Synchronized和lock的区别、volatile的介绍、Syncronized锁的作用、构造函数和构造函数重载的概念、方法覆盖和方法重载的区别、反射获取和设置对象私有字段的值的方法、通过反射创建对象的方式以及内部类的详解。 ... [详细]
  • 初识java关于JDK、JRE、JVM 了解一下 ... [详细]
  • 如何自行分析定位SAP BSP错误
    The“BSPtag”Imentionedintheblogtitlemeansforexamplethetagchtmlb:configCelleratorbelowwhichi ... [详细]
  • 本文介绍了UVALive6575题目Odd and Even Zeroes的解法,使用了数位dp和找规律的方法。阶乘的定义和性质被介绍,并给出了一些例子。其中,部分阶乘的尾零个数为奇数,部分为偶数。 ... [详细]
  • 前景:当UI一个查询条件为多项选择,或录入多个条件的时候,比如查询所有名称里面包含以下动态条件,需要模糊查询里面每一项时比如是这样一个数组条件:newstring[]{兴业银行, ... [详细]
  • 本文讨论了Kotlin中扩展函数的一些惯用用法以及其合理性。作者认为在某些情况下,定义扩展函数没有意义,但官方的编码约定支持这种方式。文章还介绍了在类之外定义扩展函数的具体用法,并讨论了避免使用扩展函数的边缘情况。作者提出了对于扩展函数的合理性的质疑,并给出了自己的反驳。最后,文章强调了在编写Kotlin代码时可以自由地使用扩展函数的重要性。 ... [详细]
  • 本文介绍了Python爬虫技术基础篇面向对象高级编程(中)中的多重继承概念。通过继承,子类可以扩展父类的功能。文章以动物类层次的设计为例,讨论了按照不同分类方式设计类层次的复杂性和多重继承的优势。最后给出了哺乳动物和鸟类的设计示例,以及能跑、能飞、宠物类和非宠物类的增加对类数量的影响。 ... [详细]
  • 先看官方文档TheJavaTutorialshavebeenwrittenforJDK8.Examplesandpracticesdescribedinthispagedontta ... [详细]
  • 纠正网上的错误:自定义一个类叫java.lang.System/String的方法
    本文纠正了网上关于自定义一个类叫java.lang.System/String的错误答案,并详细解释了为什么这种方法是错误的。作者指出,虽然双亲委托机制确实可以阻止自定义的System类被加载,但通过自定义一个特殊的类加载器,可以绕过双亲委托机制,达到自定义System类的目的。作者呼吁读者对网上的内容持怀疑态度,并带着问题来阅读文章。 ... [详细]
  • 在开发中,有时候一个业务上要求的原子操作不仅仅包括数据库,还可能涉及外部接口或者消息队列。此时,传统的数据库事务无法满足需求。本文介绍了Java中如何利用java.lang.Runtime.addShutdownHook方法来保证业务线程的完整性。通过添加钩子,在程序退出时触发钩子,可以执行一些操作,如循环检查某个线程的状态,直到业务线程正常退出,再结束钩子程序。例子程序展示了如何利用钩子来保证业务线程的完整性。 ... [详细]
  • JVM:33 如何查看JVM的Full GC日志
    1.示例代码packagecom.webcode;publicclassDemo4{publicstaticvoidmain(String[]args){byte[]arr ... [详细]
  • Java编程思想一书中第21章并发中关于线程间协作的一节中有个关于汽车打蜡与抛光的小例子(原书的704页)。这个例子主要展示的是两个线程如何通过wait ... [详细]
author-avatar
雅枝建彰3
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有