转载自https://blog.csdn.net/Scythe666/article/details/51841161 

           http://blog.csdn.net/scythe666/article/details/51700142

 jvm内存

一.运行时内存

jvm(1)内存-LMLPHP

jvm(1)内存-LMLPHP

二.程序计数器

三.Java虚拟机栈

jvm(1)内存-LMLPHP

 

此外,java中没有寄存器,因此所有的参数传递依靠操作数栈。

栈上分配,小对象(一般几十个bytes),在没有逃逸的情况下,可以直接分配在栈上。(没有逃逸是指,对象只能给当前线程使用,如果多个线程都要用,则不可以,因为栈是线程私有的。)直接分配在栈上,可以自动回收,减轻GC压力。因为栈本身比较小,大对象也不可以分配,会影响性能。

-XX:+DoEscapeAnalysis 启用逃逸分析,若非逃逸则可栈上分配。

四.本地方法栈

五. Java堆

关于TLAB

Sun Hotspot JVM为了提升对象内存分配的效率,对于所创建的线程都会分配一块独立的空间TLAB(Thread Local Allocation Buffer),其大小由JVM根据运行的情况计算而得,在TLAB上分配对象时不需要加锁,因此JVM在给线程的对象分配内存时会尽量的在TLAB上分配,在这种情况下JVM中分配对象内存的性能和C基本是一样高效的,但如果对象过大的话则仍然是直接使用堆空间分配

TLAB仅作用于新生代的Eden Space,因此在编写Java程序时,通常多个小的对象比大的对象分配起来更加高效。

六.方法区

 

直接内存

并不是虚拟机运行时数据区的一部分,也不是Java虚拟机规范中定义的内存区域,也可能导致 OOM 异常(内存区域综合>物理内存时)。NIO类,可以使用Native 函数库直接分配堆外内存,然后通过一个存储在Java 堆里面的 DirectByteBuffer 对象作为这块内存的引用进行操作。

jvm(1)内存-LMLPHP

类加载时 方法信息保存在一块称为方法区的内存中, 并不随你创建对象而随对象保存于堆中。

逻辑内存模型我们已经看到了,那当我们建立一个对象的时候是怎么进行访问的呢?

在Java 语言中,对象访问是如何进行的?对象访问在Java 语言中无处不在,是最普通的程序行为,但即使是最简单的访问,也会却涉及Java 栈、Java 堆、方法区这三个最重要内存区域之间的关联关系,如下面的这句代码:

Object obj = new Object();

假设这句代码出现在方法体中,那“Object obj”这部分的语义将会反映到Java 栈的本地变量表中,作为一个reference 类型数据出现。而“new Object()”这部分的语义将会反映到Java 堆中,形成一块存储了Object 类型所有实例数据值(Instance Data,对象中各个实例字段的数据)的结构化内存,根据具体类型以及虚拟机实现的对象内存布局(Object Memory Layout)的不同,这块内存的长度是不固定的。另外,在Java 堆中还必须包含能查找到此对象类型数据(如对象类型、父类、实现的接口、方法等)的地

址信息,这些类型数据则存储在方法区中。由于reference 类型在Java 虚拟机规范里面只规定了一个指向对象的引用,并没有定义这个引用应该通过哪种方式去定位,以及访问到Java 堆中的对象的具体位置,因此不同虚拟机实现的对象访问方式会有所不同,主流的访问方式有两种:使用句柄和直接指针

(1)如果使用句柄访问方式,Java 堆中将会划分出一块内存来作为句柄池,reference中存储的就是对象的句柄地址,而句柄中包含了对象实例数据和类型数据各自的具体地址信息,如下图所示。

jvm(1)内存-LMLPHP

(2)如果使用直接指针访问方式,

Java 堆对象的布局中就必须考虑如何放置访问类型数据的相关信息,reference 中直接存储的就是对象地址,如下图所示

jvm(1)内存-LMLPHP

这两种对象的访问方式各有优势,使用句柄访问方式的最大好处就是reference 中存储的是稳定的句柄地址,在对象被移动(垃圾收集时移动对象是非常普遍的行为)时只会改变句柄中的实例数据指针,而reference 本身不需要被修改。使用直接指针访问方式的最大好处就是速度更快,它节省了一次指针定位的时间开销,由于对象的访问在Java 中非常频繁,因此这类开销积少成多后也是一项非常可观的执行成本。就本书讨论的主要虚拟机Sun HotSpot 而言,它是使用第二种方式进行对象访问的,但从整个软件开发的范围来看,各种语言和框架使用句柄来访问的情况也十分常见。

七. 堆溢出、栈溢出原因及实例,线上如何排查

 (1)栈溢出

递归,容易引起栈溢出stackoverflow;因为方法循环调用,方法调用会不断创建栈帧。 
造成栈溢出的几种情况: 
1)递归过深 
2)数组、List、map数据过大 
3 ) 创建过多线程

对于Java虚拟机栈和本地方法栈,Java虚拟机规范规定了两种异常状况:

① 线程请求深度>虚拟机所允许的深度,将抛出StackOverFlowError(SOF)异常;

② 如果虚拟机可动态扩展,且扩展时无法申请到足够的内存,就会抛出OutOfMemoryError(OOM)异常。

jvm(1)内存-LMLPHP

解决栈溢出的方法 

package com.yhj.jvm.memory.stack;

/**

 * @Described:栈层级不足探究

 * @VM args:-Xss128k

 * @FileNmae com.yhj.jvm.memory.stack.StackOverFlow.java

 */

public class StackOverFlow {

    private int i ;

    public void plus() {

       i++;

       plus();

    }


    /**
     * @param args
     *
     */

    public static void main(String[] args) {

       StackOverFlow stackOverFlow = new StackOverFlow();

       try {

           stackOverFlow.plus();

       } catch (Exception e) {

           System.out.println("Exception:stack length:"+stackOverFlow.i);

           e.printStackTrace();

       } catch (Error e) {

           System.out.println("Error:stack length:"+stackOverFlow.i);

           e.printStackTrace();

       }

    }

}

(2)堆溢出

如果在堆中没有内存完成实例分配,且堆无法扩展时,将抛出OOM异常。

在方法区也会抛出 OOM 异常。

package OOM;

import java.util.ArrayList;
import java.util.List;


public class App1 {

    static class OOMClass {
        long[] num = new long[10240];
    }

    public static void main(String[] args) {
        List<OOMClass> list = new ArrayList<>();
        while (true) {
            list.add(new OOMClass());
        }
    }


}

怎么解决?

 -vmargs  //虚拟机设置

     -Xms40m //初始内存

     -Xmx256m //最大内存

     -Xmn16m //最小内存

     -XX:PermSize=128M //非堆内存

     -XX:MaxPermSize=256M

10-03 21:26