详谈jvm线程栈空间内存分配位置

目录
  • jvm线程栈空间内存分配位置
    • JVM配置如下
    • 测试截的一些图片如下
    • 测试代码
  • jvm栈大小设置
    • 1、栈内存大小设置
    • 2、递归调用

jvm线程栈空间内存分配位置

jvm的线程栈申请的内存空间属于堆外内存,是向操作系统申请的,也不是JVM直接内存,虽然类似。

JVM能创建的线程数需要的内存,不是JVM运行内存,堆内存,直接内存,而是操作系统剩余的可用内存,这个也决定了能创建的线程数,如果内存不够用,创建线程的时候便会出现内存溢出的错误。

在操作系统的可用内存不足的情况下,想要创建更多的线程,可以考虑减少线程栈的空间大小(-Xss),但是不建议过小,栈尝试减小容易栈溢出错误。

--------------------------分割线--------------------------

后来有次早上5点睡不着,起床做了个测试验证,在这里补充下这个测试说明,当时只是发了个朋友圈,过得有点久了现在我也只从手机上找到几张图片贴上来:

JVM配置如下

设置最大堆10m,线程栈大小10m,直接内存5m。

创建1000条线程,调用10w次函数(调用次数再过多,栈深不够就溢出了),疯狂压栈但不出栈(递归调用)。然后看内存,物理内存占用7g多,有次是5g多。没有堆溢出,也没有直接内存溢出。说明线程栈内存在堆外分配,不属于堆内存(线程对象还是分配在堆内存空间),也不属于直接内存部分。

测试截的一些图片如下

JVM配置

main方法创建1条线程并运行:

main方法创建1000条线程并运行:

测试代码

/**
 * @Auther: 许晓东
 * @Date: 19-8-3 05:14
 * @Description:
 */
public class ThreadStackTest {
    static Map<Integer, Thread> map = new HashMap<>();
    static Runnable runnable = new Runnable() {
        @Override
        public void run() {
            try {
                recursive(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    };

    private static void recursive(int num) throws InterruptedException {
        if (num == 20000) {
            Thread.sleep(Integer.MAX_VALUE);
        }else {
            recursive(++num);
        }
    }

    public static void main(String[] args) throws InterruptedException {
        System.gc();
        int nums = 1000;
        //int nums = 1;
        for (int i = 0; i < nums; i++) {
            map.put(i, new Thread(runnable));
        }

        for (Thread thread :
                map.values()) {
            thread.start();
        }

        System.out.println(String.format("创建%d条线程后gc情况:", nums));
        System.gc();
        Thread.sleep(Integer.MAX_VALUE);
    }
}

jvm栈大小设置

1、栈内存大小设置

栈内存为线程私有的空间,每个线程都会创建私有的栈内存。栈空间内存设置过大,创建线程数量较多时会出现栈内存溢出StackOverflowError。同时,栈内存也决定方法调用的深度,栈内存过小则会导致方法调用的深度较小,如递归调用的次数较少。

-Xss:如-Xss128k

通常只有几百K

决定了函数调用的深度

每个线程都有独立的栈空间

局部变量、参数 分配在栈上

2、递归调用

package com.thread.study;
public class Stack {
  private static int count=0;
  public static void recursion(long a,long b,long c){
   long e=1,f=2,g=3,h=4,i=5,k=6,q=7,x=8,y=9,z=10;
   count++;
   recursion(a,b,c);
  }
  public static void main(String args[]){
   try{
    recursion(0L,0L,0L);
   }catch(Throwable e){
    System.out.println("deep of calling = "+count);
    e.printStackTrace();
   }
  }
}
  • -Xss128k:deep of calling = 306
  • -Xss256k:deep of calling = 761

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • JVM中有哪些内存区域及其作用

    前言 之前我们探讨过一个.class文件是如何被加载到jvm中的.但是jvm内又是如何划分内存的呢?这个内被加载到了那一块内存中?jvm内存划分也是面试当中必被问到的一个面试题. 什么是jvm内存区域划分? 其实这个问题非常简单,JVM在运行我们写好的代码时,他是必须使用多块内存空间的,不同的内存空间用来放不同的数据,然后配合我们写的代码流程,才能让我们的系统运行起来. 举个最简单的例子,比如咱们现在知道了JVM会加载类到内存里来供后续运行,那么我问问大家,这些类加载到内存以后,放到哪儿去了呢?

  • JVM入门之JVM内存结构内容详解

    一.java代码编译执行过程 源码编译:通过Java源码编译器将Java代码编译成JVM字节码(.class文件) 类加载:通过ClassLoader及其子类来完成JVM的类加载 类执行:字节码被装入内存,进入JVM虚拟机,被解释器解释执行   注:Java平台由Java虚拟机和Java应用程序接口搭建,Java语言则是进入这个平台的通道,   用Java语言编写并编译的程序可以运行在这个平台上 二.JVM简介 1.java程序经过一次编译之后,将java代码编译为字节码也就是class文件,然

  • JVM内存结构划分实例解析

    这篇文章主要介绍了JVM内存结构划分实例解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 数据区域划分 运行时内存区域划分:程序计数器.虚拟机栈.本地方法栈.堆.方法区 程序计数器 线程私有 通过寄存器实现 不会存在运行溢出 当前线程所执行的行号指示器,记住下一条JVM指令的执行地址 虚拟机栈 垃圾回收不涉及栈内存 栈内存是线程私有的,可以理解为线程运行需要的内存空间 栈由栈帧组成,每个栈帧代表一个方法执行时需要的内存(参数,局部变量,返回地

  • JVM内存区域划分相关原理详解

    学过C语言的朋友都知道C编译器在划分内存区域的时候经常将管理的区域划分为数据段和代码段,数据段包括堆.栈以及静态数据区.那么在Java语言当中,内存又是如何划分的呢? 由于Java程序是交由JVM执行的,所以我们在谈Java内存区域划分的时候事实上是指JVM内存区域划分.在讨论JVM内存区域划分之前,先来看一下Java程序具体执行的过程: 如上图所示,首先Java源代码文件(.java后缀)会被Java编译器编译为字节码文件(.class后缀),然后由JVM中的类加载器加载各个类的字节码文件,加

  • 详谈jvm线程栈空间内存分配位置

    目录 jvm线程栈空间内存分配位置 JVM配置如下 测试截的一些图片如下 测试代码 jvm栈大小设置 1.栈内存大小设置 2.递归调用 jvm线程栈空间内存分配位置 jvm的线程栈申请的内存空间属于堆外内存,是向操作系统申请的,也不是JVM直接内存,虽然类似. JVM能创建的线程数需要的内存,不是JVM运行内存,堆内存,直接内存,而是操作系统剩余的可用内存,这个也决定了能创建的线程数,如果内存不够用,创建线程的时候便会出现内存溢出的错误. 在操作系统的可用内存不足的情况下,想要创建更多的线程,可

  • JVM对象创建和内存分配原理解析

    创建对象 当 JVM 收到一个 new 指令时,会检查指令中的参数在常量池是否有这个符号的引用,还会检查该类是否已经被加载过了,如果没有的话则要进行一次类加载. 接着就是分配内存了,通常有两种方式: 指针碰撞 空闲列表 使用指针碰撞的前提是堆内存是完全工整的,用过的内存和没用的内存各在一边每次分配的时候只需要将指针向空闲内存一方移动一段和内存大小相等区域即可. 当堆中已经使用的内存和未使用的内存互相交错时,指针碰撞的方式就行不通了,这时就需要采用空闲列表的方式.虚拟机会维护一个空闲的列表,用于记

  • 详解jvm对象的创建和分配

    对象的创建 创建方式 1. new 关键字直接创建. new ObjectName(). 2.通过 Class 反射对象的 newInstance() 方法.ObjectName  obj  =  ObjectName.class.newInstance(). 3.通过 Class 反射对象获取 Constructor 类,再调用其 newInstance() 方法. ObjectName obj = ObjectName.class.getConstructor.newInstance().

  • JVM内存分配及String常用方法解析

    一,JVM内存分配和常量池 ​ 在介绍String类之前,先来简单分析一下在JVM中,对内存的使用是如何进行分配的.如下图所示(注意:在jdk1.8之后便没有方法区了): ​ ​ 如上JVM将内存分为多个不同的区域,这些区域都有各自的用途.创建和销毁的时间,有些区域随虚拟机进程的启动而存在,有些区域则是依赖用户线程的启动和结束来建立和销毁. ​ 区域名称的说明: 1.1,方法区: ​ 属于数据共享内存区域,存储已被虚拟机加载的类信息.常量.静态变量.即时编译器编译后的代码等数据. 1.2,虚拟机

  • 浅谈java+内存分配及变量存储位置的区别

    Java内存分配与管理是Java的核心技术之一,之前我们曾介绍过Java的内存管理与内存泄露以及Java垃圾回收方面的知识,今天我们再次深入Java核心,详细介绍一下Java在内存分配方面的知识.一般Java在内存分配时会涉及到以下区域: ◆寄存器:我们在程序中无法控制 ◆栈:存放基本类型的数据和对象的引用,但对象本身不存放在栈中,而是存放在堆中(new 出来的对象) ◆堆:存放用new产生的数据 ◆静态域:存放在对象中用static定义的静态成员 ◆常量池:存放常量 ◆非RAM存储:硬盘等永久

  • ASP.NET堆和栈一之基本概念和值类型内存分配

    ".NET的堆和栈"系列: ASP.NET堆和栈一之基本概念和值类型内存分配 ASP.NET堆和栈二之值类型和引用类型参数传递和内存分配 ASP.NET堆和栈三之引用类型对象拷贝和内存分配 ASP.NET堆和栈四之对托管和非托管资源垃圾回收和内存分配 当我们对.NET Framework的一些基本面了解之后,实际上,还是很有必要了解一些更底层的知识.比如.NET Framework是如何进行内存管理的,是如何垃圾回收的......这样,我们才能写出更高性能的程序. 在.NET Fram

  • Java 内存分配深入理解

    Java 内存分配深入理解 本文将由浅入深详细介绍Java内存分配的原理,以帮助新手更轻松的学习Java.这类文章网上有很多,但大多比较零碎.本文从认知过程角度出发,将带给读者一个系统的介绍. 进入正题前首先要知道的是Java程序运行在JVM(Java  Virtual Machine,Java虚拟机)上,可以把JVM理解成Java程序和操作系统之间的桥梁,JVM实现了Java的平台无关性,由此可见JVM的重要性.所以在学习Java内存分配原理的时候一定要牢记这一切都是在JVM中进行的,JVM是

  • 详谈java线程与线程、进程与进程间通信

    线程与线程间通信 一.基本概念以及线程与进程之间的区别联系: 关于进程和线程,首先从定义上理解就有所不同 1.进程是什么? 是具有一定独立功能的程序.它是系统进行资源分配和调度的一个独立单位,重点在系统调度和单独的单位,也就是说进程是可以独 立运行的一段程序. 2.线程又是什么? 线程进程的一个实体,是CPU调度和分派的基本单位,他是比进程更小的能独立运行的基本单位,线程自己基本上不拥有系统资源. 在运行时,只是暂用一些计数器.寄存器和栈 . 他们之间的关系 1.一个线程只能属于一个进程,而一个

  • java程序运行时内存分配详解

    一. 基本概念 每运行一个java程序会产生一个java进程,每个java进程可能包含一个或者多个线程,每一个Java进程对应唯一一个JVM实例,每一个JVM实例唯一对应一个堆,每一个线程有一个自己私有的栈.进程所创建的所有类的实例(也就是对象)或数组(指的是数组的本身,不是引用)都放在堆中,并由该进程所有的线程共享.Java中分配堆内存是自动初始化的,即为一个对象分配内存的时候,会初始化这个对象中变量.虽然Java中所有对象的存储空间都是在堆中分配的,但是这个对象的引用却是在栈中分配,也就是说

  • java 字符串内存分配的分析与总结(推荐)

    经常在网上各大版块都能看到对于java字符串运行时内存分配的探讨,形如:String a = "123",String b = new String("123"),这两种形式的字符串是存放在什么地方的呢,其实这两种形式的字符串字面值"123"本身在运行时既不是存放在栈上,也不是存放在堆上,他们是存放在方法区中的某个常量区,并且对于相同的字符串字面值在内存中只保留一份.下面我们将以实例来分析. 1.==运算符作用在两个字符串引用比较的两个案例: p

随机推荐