code: public T get() {
          T object;
  
          while (true) {
              // 1. get the next free object
              int p = m_put.get();
              int g = m_get.get();
              if (g >= p) {
                  // get index hits the put index, implying empty pool
                  if (p >= LIMIT) {
                      // put pointer wrapped around. rewind both indexes.
                      m_get.set(0);
                      m_put.set(0);
                  }
                  return null;
              }
  
              // 2. conquer this cell by incrementing the get index
              if (!m_get.compareAndSet(g, g + 1)) {
                  // some other thread got here first. try again.
                  continue;
              }
  
              // 3. retrieve the object at the get index
              int index = g % m_arraySize;
              AtomicReference<T> reference = m_array[index];
              object = reference.get();
              if (object == null) {
                  // getting here is rare. it happens when:
                  // 1. put() is called on an empty pool (p==g)
                  // 2. put() increments the put pointer (p==g+1)
                  // 3. get() is called and finds g < p
                  // 4. get() takes the object at g (which is null)
                  // 5. put() puts the object at g
                  continue;
              }
  
              // 4. change the reference to null so no other get() takes the
              // same element, and no other put() replaces it with another.
              if (reference.compareAndSet(object, null)) {
                  return object;
              }
  
              // getting here is rare. the object that was supposedly conquered
              // locally was either replaced by a different object or replaced
              // with null, in the same cell.
          }
      }
docs: gets an object from the pool
  
  @return the pooled object