User user = new User();

对象的引用存放在栈中,对象存放在堆中。堆的存储结构和栈是不同的,堆在内存中并不是一块连续的区域,它是分散的(物理上是分散,但逻辑上是连续的,大家好好体会一下);虚拟机通过栈中引用的指引在堆中找到所需要的对象。
  
  在虚拟机遇到一条new的指令的时候,经过一系列的操作过后虚拟机就要为该新生对象分配内存空间了,那么问题来了,这么散,虚拟机要怎么知道如何分配呢?分配的方式有两种:指针碰撞和空闲列表。
  
  指针碰撞是将内存逻辑上分为两边,一边是空闲的,一边是在用的,指针指向分界点,当需要分配内存的时候只要移动指针即可。但这种只适用于内存规整的情况下,也就是刚刚说的分两边。一般用在Serial,PaeNew等垃圾收集器中,也就是堆中的新生代中【新生代又分为Eden区:幸存者0区:幸存者1区=8:1:1,新生代GC时采用复制算法(优点:没有内存碎片;缺点:浪费空间)】。
  
  那么空闲列表说的就是在内存不是规整的情况下,虚拟机必须维护一个列表,用于记录哪些内存是可用的,在需要进行分配的时候就从列表中找到一块足够大小的空间进行分配,并且更新列表。该方法适用于像CMS这种基于Mark-Sweep的垃圾收集器,适用于堆中的老年区【老年区GC时采用标记清除算法(优点:节约空间;缺点:1.两次扫描:一次标记,一次清除,浪费时间 2.会产生内存碎片)和标记压缩算法(优点:节约空间,没有内存碎片;缺点:两次扫描:一次标记,一次清除,整理操作,浪费时间)】。
  
  垃圾收集器,也就是GC。写过java的都知道,java程序很少需要我们去自己释放资源,原因就是这个GC机制了。

总结:

  • 存放new创建的对象和数组;
  • 在运行时动态分配内存(比如 new()),较慢,但灵活;
  • 是不连续的内存区域,堆内存空间不足的时候会产生OutOfMemoryError错误
  • 由Java虚拟机的自动垃圾回收器来管理。