首页 > 技术文章 > java基础---JVM---java内存区域与内存溢出问题

buptyuhanwen 2018-08-03 14:13 原文

===?虚拟机运行时候内存主要有几块区域,是线程相关的吗,分别有什么作用?
程序计数器:线程私有,用来记录当前线程所执行的字节码的行数,使得在多线程执行的情况下线程切换后能回到正确的执行位置。程序计数器不会内存溢出。如果执行java方法,计数器会记录执行的字节码的行数;如果执行的是native方法,计数器值为空。
虚拟机栈:线程独有,描述了java方法执行的内存模型,存储java方法执行的相关信息,每个方法的执行对应一个栈帧在虚拟机栈的入栈和出栈。具体包括有1.局部变量表:基本数据类型和引用类型。2.操作数栈,保存计算中间结果的。就是指令不断的从里面拿数据计算后压入。3.动态链接。4.方法的出口
可能抛出的异常包括:stackoverflow线程请求的栈深度大于虚拟机允许的深度比如递归调用太深的时候会抛这个异常,当方法的入参很多的时候,会加大栈帧的大小,这样会导致栈的深度变小;outofmemory异常内存溢出的异常。因为这两个异常都是继承自error,所以catch的时候可以catch Throwable
虚拟机参数设置:-Xss128K设置每个线程创建的时候虚拟机栈的大小,这个值设置的越小就能够创建越多的线程。
堆:线程共享,存放实例对象。垃圾收集器的主要管理部分
----方法区:类信息,常量池(常量,静态变量),方法数据,方法代码等
运行时常量池 
直接内存:不受虚拟机控制的堆外内存,没有垃圾回收的限制。使用堆内内存的时候想要把数据flush到远端的话要经过复制到堆外再传输,直接使用堆外内存的话就少了这一步了。
 
 
 
 
图1 进程地址空间分布
图1是进程地址空间分布的简单表示。代码存储了用户程序的所有可执行代码,在程序正常执行的情况下,程序计数器(PC指针)只会在代码段和操作系统地址空间(内核态)内寻址。数据段内存储了用户程序的全局变量,文字池等。栈空间存储了用户程序的函数栈帧(包括参数、局部数据等),实现函数调用机制,它的数据增长方向是低地址方向。堆空间存储了程序运行时动态申请的内存数据等,数据增长方向是高地址方向。除了代码段和受操作系统保护的数据区域,其他的内存区域都可能作为缓冲区,因此缓冲区溢出的位置可能在数据段,也可能在堆、栈段。如果程序的代码有软件漏洞,恶意程序会“教唆”程序计数器从上述缓冲区内取指,执行恶意程序提供的数据代码!本文分析并实现栈溢出攻击方式。

===jdk8之前的永久代是什么样的,如何设置大小?
http://blog.csdn.net/a58220655/article/details/75330365
jdk6:永久代在堆中,永久代中有方法区,方法区具体存的有类信息,常量,静态变量,运行时常量池,方法数据,方法代码。
jdk7:方法区和永久代分离,在堆中另外开了一片区域用来存放方法区和运行时常量池。
jdk8:移除掉永久代,使用堆外内存metaspace来保存方法区。
可以通过-XX:MaxPermSize来设置永久代的大小,如果类的元数据超过设置的大小就会内存溢出。
 
===java虚拟机的元空间Metaspace,metaspace是如何进行内存管理的呢?如何设置元空间大小?元空间存在的问题?
jdk8里面永久带消失了,取而代之的是放在内存中的metaspace元空间。那么元空间的最大可分配空间就是系统的内存空间,其大小可以手动设置也可以自动根据元数据大小改变。
元空间内存由元空间虚拟机进行管理,采取组块分配的形式来进行内存管理。元空间虚拟机维护了一张空闲组块表,当类加载器需要组块的时候就会和这张表申请空闲组块,类加载器不再存活了就返回组块。
元空间是可以设置大小的,-XX:MaxMetaspaceSize可以设置元空间最大的大小。
元空间虚拟机分配组块,组块大小由类加载器类型决定。但是类信息不是固定大小,比如向某个类添加了方法,类信息就会变多。这个时候分配的区块大小不够,就可能存在碎片。元空间虚拟机当前不支持压缩,所以碎片化是大问题。
 
===?请区分常量池,字符串常量池,运行时常量池?
http://blog.csdn.net/sunshine__me/article/details/49992909
1.常量池是Class文件中的一部分,存储了类,方法,接口中的常量,也包括字符串常量池。Class文件只是一个表,表里面由以8字节为单位的0101数据组成。
2.运行时常量池是方法区中的一部分,所有线程共享,虚拟机加载Class文件后将常量池中的数据放入到运行时常量池中
 
2.3虚拟机对象创建
2.3.1对象的创建
===?对象创建步骤
1.运行期常量池中寻找class对象,判断虚拟机是否已经加载了类信息,如果没有加载就加载类信息。
2.为对象在堆中分配内存空间,创建其他对象的引用,并且全部初始化为0
3.进行初始化,进行静态代码块的初始化,静态变量的初始化。
 
 
===?String.intern()方法有什么作用呢?是native方法吗?
1.先判断是否常量池中存在此字符串,如果存在那么返回引用。
2.否则添加到常量池中并返回引用。
 
 
===?分析String s = "a" + "c"以及String a = "a" ;String s = a + "c";
1)String s = "a" + "c"
先创建a和c以及ac加入字符串常量池
然后将s指向字符串常量池中的ac
2)String a = "a" ;
String s = a + "c";
先创建a加入字符串常量池
将a指向常量池中的a
因为第二行存在变量a,所以最后再堆中创建一个对象,s指向堆中的对象而不指向常量池中的对象,并且并不会创建ac存入常量池。
 
 
 

推荐阅读