这里有一点要重点说明,在多线程情况下,给每个线程的栈分配的内存越大,反而越容易产生内存产生内存溢出一场。操作系统为每个进程分配的内存是有限制的,虚拟机提供了参数来控制Java堆和方法区这两部分内存的大值,忽略掉程序计数器消耗的内存(很小),以及进程本身消耗的内存,剩下的内存便给了虚拟机栈和本地方法栈,每个线程分配到的栈容量越大,可以建立的线程数量自然越少。因此,如果是建立过多的线程导致的内存溢出,在不能减少线程数的情况下,只能通过减少大堆和每个线程的栈容量来换取更多的线程。
  另外,由于Java堆内也可能发生内存泄露(Memory Leak),这里简要说明一下内存泄露和内存溢出的区别:
  内存泄漏是指分配出去的内存没有被回收回来,由于失去了对该内存区域的控制,因而造成了资源的浪费。Java中一般不会产生内存泄漏,因为有垃圾回收器自动回收垃圾,但这也不,当我们new了对象,并保存了其引用,但是后面一直没用它,而垃圾回收器又不会去回收它,这会造成内存泄漏。
  内存溢出是指程序所需要的内存超过了系统所能分配的内存(包括动态扩展)的上限。
  对象实例化分析
  对内存分配情况分析常见的示例便是对象实例化:
  Object obj = new Object();
  这段代码的执行会涉及java栈、Java堆、方法区三个重要的内存区域。假设该语句出现在方法体中,及时对JVM虚拟机不了解的Java使用这,应该也知道obj会作为引用类型(reference)的数据保存在Java栈的本地变量表中,而会在Java堆中保存该引用的实例化对象,但可能并不知道,Java堆中还必须包含能查找到此对象类型数据的地址信息(如对象类型、父类、实现的接口、方法等),这些类型数据则保存在方法区中。
  另外,由于reference类型在Java虚拟机规范里面只规定了一个指向对象的引用,并没有定义这个引用应该通过哪种方式去定位,以及访问到Java堆中的对象的具体位置,因此不同虚拟机实现的对象访问方式会有所不同,主流的访问方式有两种:使用句柄池和直接使用指针。
  通过句柄池访问的方式如下:


  通过直接指针访问的方式如下:

  这两种对象的访问方式各有优势,使用句柄访问方式的大好处是reference中存放的是稳定的句柄地址,在对象被移动(垃圾收集时移动对象是非常普遍的行为)时只会改变句柄中的实例数据指针,而reference本身不需要修改。使用直接指针访问方式的大好处是速度快,它节省了一次指针定位的时间开销。目前Java默认使用的HotSpot虚拟机采用的便是是第二种方式进行对象访问的。