title: What is numpy empty doing under the hood when I allocate a massive ndarray?
body: <p>I was looking at how much space numpy arrays consume in memory and I noticed a peculiar behavior: </p>
  
  <p>When I ran <code>x = np.empty((1000000, 7, 64, 64), dtype=np.uint8)</code></p>
  
  <p>My computer with 16GB of memory did not crash. Instead it was sailing smoothly with 2GB of memory allocated.</p>
  
  <p>This numpy array should weigh in at 26.70 GB, but something lazy seems to be happening. When I add one, then the laziness stops immediately, and my program hangs and them gets a <code>MemoryError</code>.</p>
  
  <p>I'm wondering how numpy does this under the hood. </p>
  
  <p>I took a look at <code>numpy.core.multiarray</code>, and found <code>numpy/core/src/multiarray/multiarraymodule.c</code> with this bit of code that seems to be the definition of empty: </p>
  
  <pre><code>static PyObject *
  array_empty(PyObject *NPY_UNUSED(ignored), PyObject *args, PyObject *kwds)
  {
  
      static char *kwlist[] = {"shape","dtype","order",NULL};
      PyArray_Descr *typecode = NULL;
      PyArray_Dims shape = {NULL, 0};
      NPY_ORDER order = NPY_CORDER;
      npy_bool is_f_order;
      PyArrayObject *ret = NULL;
  
      if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&amp;|O&amp;O&amp;", kwlist,
                  PyArray_IntpConverter, &amp;shape,
                  PyArray_DescrConverter, &amp;typecode,
                  PyArray_OrderConverter, &amp;order)) {
          goto fail;
      }
  
      switch (order) {
          case NPY_CORDER:
              is_f_order = NPY_FALSE;
              break;
          case NPY_FORTRANORDER:
              is_f_order = NPY_TRUE;
              break;
          default:
              PyErr_SetString(PyExc_ValueError,
                              "only 'C' or 'F' order is permitted");
              goto fail;
      }
  
      ret = (PyArrayObject *)PyArray_Empty(shape.len, shape.ptr,
                                              typecode, is_f_order);
  
      PyDimMem_FREE(shape.ptr);
      return (PyObject *)ret;
  
   fail:
      Py_XDECREF(typecode);
      PyDimMem_FREE(shape.ptr);
      return NULL;
  }
  </code></pre>
  
  <p>I'm wondering how this laziness is achieved in C, and where else it will pop up in numpy. </p>