diff --git a/addon/-private/system/model/internal-model.js b/addon/-private/system/model/internal-model.js
index ccc2ee168ef..3f8f8ad3c94 100644
--- a/addon/-private/system/model/internal-model.js
+++ b/addon/-private/system/model/internal-model.js
@@ -431,14 +431,10 @@ export default class InternalModel {
   */
   _directlyRelatedInternalModels() {
     let array = [];
-    this.type.eachRelationship((key, relationship) => {
-      if (this._relationships.has(key)) {
-        let relationship = this._relationships.get(key);
-        let localRelationships = relationship.members.toArray();
-        let serverRelationships = relationship.canonicalMembers.toArray();
-
-        array = array.concat(localRelationships, serverRelationships);
-      }
+    this._relationships.forEach((name, rel) => {
+      let local = rel.members.toArray();
+      let server = rel.canonicalMembers.toArray();
+      array = array.concat(local, server);
     });
     return array;
   }
@@ -492,6 +488,7 @@ export default class InternalModel {
   unloadRecord() {
     this.send('unloadRecord');
     this.dematerializeRecord();
+
     if (this._scheduledDestroy === null) {
       this._scheduledDestroy = run.schedule('destroy', this, '_checkForOrphanedInternalModels');
     }
@@ -511,6 +508,7 @@ export default class InternalModel {
     if (this.isDestroyed) { return; }
 
     this._cleanupOrphanedInternalModels();
+
   }
 
   _cleanupOrphanedInternalModels() {
@@ -533,6 +531,9 @@ export default class InternalModel {
     assert("Cannot destroy an internalModel while its record is materialized", !this._record || this._record.get('isDestroyed') || this._record.get('isDestroying'));
 
     this.store._internalModelDestroyed(this);
+
+    this._relationships.forEach((name, rel) => rel.destroy());
+
     this._isDestroyed = true;
   }
 
@@ -889,14 +890,10 @@ export default class InternalModel {
     @private
    */
   removeFromInverseRelationships(isNew = false) {
-    this.eachRelationship((name) => {
-      if (this._relationships.has(name)) {
-        let rel = this._relationships.get(name);
-
-        rel.removeCompletelyFromInverse();
-        if (isNew === true) {
-          rel.clear();
-        }
+    this._relationships.forEach((name, rel) => {
+      rel.removeCompletelyFromInverse();
+      if (isNew === true) {
+        rel.clear();
       }
     });
 
@@ -918,17 +915,28 @@ export default class InternalModel {
     and destroys any ManyArrays.
    */
   destroyRelationships() {
-    this.eachRelationship((name, relationship) => {
-      if (this._relationships.has(name)) {
-        let rel = this._relationships.get(name);
+    this._relationships.forEach((name, rel) => {
+      if (rel._inverseIsAsync()) {
+        rel.removeInternalModelFromInverse(this);
         rel.removeInverseRelationships();
+      } else {
+        rel.removeCompletelyFromInverse();
       }
     });
 
     let implicitRelationships = this._implicitRelationships;
     this.__implicitRelationships = null;
     Object.keys(implicitRelationships).forEach((key) => {
-      implicitRelationships[key].removeInverseRelationships();
+      let rel = implicitRelationships[key];
+
+      if (rel._inverseIsAsync()) {
+        rel.removeInternalModelFromInverse(this);
+        rel.removeInverseRelationships();
+      } else {
+        rel.removeCompletelyFromInverse();
+      }
+
+      rel.destroy();
     });
   }
 
diff --git a/addon/-private/system/relationships/state/create.js b/addon/-private/system/relationships/state/create.js
index f0fe88c7cbc..0acc2b86adf 100644
--- a/addon/-private/system/relationships/state/create.js
+++ b/addon/-private/system/relationships/state/create.js
@@ -46,6 +46,13 @@ export default class Relationships {
     return !!this.initializedRelationships[key];
   }
 
+  forEach(cb) {
+    let rels = this.initializedRelationships;
+    Object.keys(rels).forEach(name => {
+      cb(name, rels[name]);
+    });
+  }
+
   get(key) {
     let relationships = this.initializedRelationships;
     let relationship = relationships[key];
diff --git a/addon/-private/system/relationships/state/has-many.js b/addon/-private/system/relationships/state/has-many.js
index ed260283a01..55c89af15c3 100644
--- a/addon/-private/system/relationships/state/has-many.js
+++ b/addon/-private/system/relationships/state/has-many.js
@@ -288,6 +288,20 @@ export default class ManyRelationship extends Relationship {
       this.updateInternalModelsFromAdapter(internalModels);
     }
   }
+
+  destroy() {
+    super.destroy();
+    let manyArray = this._manyArray;
+    if (manyArray) {
+      manyArray.destroy();
+    }
+
+    let proxy = this.__loadingPromise;
+
+    if (proxy) {
+      proxy.destroy();
+    }
+  }
 }
 
 function setForArray(array) {
diff --git a/addon/-private/system/relationships/state/relationship.js b/addon/-private/system/relationships/state/relationship.js
index 20e7ea950f2..951b1a49bb3 100644
--- a/addon/-private/system/relationships/state/relationship.js
+++ b/addon/-private/system/relationships/state/relationship.js
@@ -83,6 +83,13 @@ export default class Relationship {
     return this.internalModel.modelName;
   }
 
+  _inverseIsAsync() {
+    if (!this.inverseKey || !this.inverseInternalModel) {
+      return false;
+    }
+    return this.inverseInternalModel._relationships.get(this.inverseKey).isAsync;
+  }
+
   removeInverseRelationships() {
     if (!this.inverseKey) { return; }
 
@@ -174,7 +181,7 @@ export default class Relationship {
       let relationship = relationships[this.inverseKeyForImplicit];
       if (!relationship) {
         relationship = relationships[this.inverseKeyForImplicit] =
-          new Relationship(this.store, internalModel, this.key,  { options: {} });
+          new Relationship(this.store, internalModel, this.key,  { options: { async: this.isAsync } });
       }
       relationship.addCanonicalInternalModel(this.internalModel);
     }
@@ -215,7 +222,7 @@ export default class Relationship {
         internalModel._relationships.get(this.inverseKey).addInternalModel(this.internalModel);
       } else {
         if (!internalModel._implicitRelationships[this.inverseKeyForImplicit]) {
-          internalModel._implicitRelationships[this.inverseKeyForImplicit] = new Relationship(this.store, internalModel, this.key,  { options: {} });
+          internalModel._implicitRelationships[this.inverseKeyForImplicit] = new Relationship(this.store, internalModel, this.key,  { options: { async: this.isAsync } });
         }
         internalModel._implicitRelationships[this.inverseKeyForImplicit].addInternalModel(this.internalModel);
       }
@@ -453,4 +460,7 @@ export default class Relationship {
   }
 
   updateData() {}
+
+  destroy() {
+  }
 }
diff --git a/tests/integration/records/rematerialize-test.js b/tests/integration/records/rematerialize-test.js
index 743db76a36b..b7a71c0be8f 100644
--- a/tests/integration/records/rematerialize-test.js
+++ b/tests/integration/records/rematerialize-test.js
@@ -146,22 +146,47 @@ test("an async has many relationship to an unloaded record can restore that reco
   // disable background reloading so we do not re-create the relationship.
   env.adapter.shouldBackgroundReloadRecord = () => false;
 
-  env.adapter.findRecord = function() {
-    assert.ok('adapter called');
-    return Ember.RSVP.Promise.resolve({
-      data: {
-        type: 'boat',
-        id: '1',
-        attributes: {
-          name: "Boaty McBoatface"
-        },
-        relationships: {
-          person: {
-            data: { type: 'person', id: '1' }
-          }
-        }
+  const BOAT_ONE = {
+    type: 'boat',
+    id: '1',
+    attributes: {
+      name: "Boaty McBoatface"
+    },
+    relationships: {
+      person: {
+        data: { type: 'person', id: '1' }
       }
-    });
+    }
+  };
+
+  const BOAT_TWO = {
+    type: 'boat',
+    id: '2',
+    attributes: {
+      name: 'Some other boat'
+    },
+    relationships: {
+      person: {
+        data: { type: 'person', id: '1' }
+      }
+    }
+  };
+
+  env.adapter.findRecord = function(store, model, param) {
+    assert.ok('adapter called');
+
+    let data;
+    if (param === '1') {
+      data = BOAT_ONE;
+    } else if (param === '1') {
+      data = BOAT_TWO;
+    } else {
+      throw new Error(`404: no such boat with id=${param}`);
+    }
+
+    return {
+      data
+    };
   }
 
   run(function() {
@@ -186,29 +211,7 @@ test("an async has many relationship to an unloaded record can restore that reco
 
   run(function() {
     env.store.push({
-      data: [{
-        type: 'boat',
-        id: '2',
-        attributes: {
-          name: 'Some other boat'
-        },
-        relationships: {
-          person: {
-            data: { type: 'person', id: '1' }
-          }
-        }
-      }, {
-        type: 'boat',
-        id: '1',
-        attributes: {
-          name: 'Boaty McBoatface'
-        },
-        relationships: {
-          person: {
-            data: { type: 'person', id: '1' }
-          }
-        }
-      }]
+      data: [BOAT_ONE, BOAT_TWO]
     });
   });
 
@@ -220,22 +223,16 @@ test("an async has many relationship to an unloaded record can restore that reco
   assert.equal(env.store.hasRecordForId('boat', 1), true, 'The boat is in the store');
   assert.equal(env.store._internalModelsFor('boat').has(1), true, 'The boat internalModel is loaded');
 
-  let boats = run(() => {
-    return adam.get('boats');
-  });
+  let boats = run(() => adam.get('boats'));
 
   assert.equal(boats.get('length'), 2, 'Before unloading boats.length is correct');
 
-  run(function() {
-    boaty.unloadRecord();
-  });
+  run(() => boaty.unloadRecord());
 
   assert.equal(env.store.hasRecordForId('boat', 1), false, 'The boat is unloaded');
   assert.equal(env.store._internalModelsFor('boat').has(1), true, 'The boat internalModel is retained');
 
-  let rematerializedBoaty = run(() => {
-    return rematerializedBoaty = adam.get('boats').objectAt(1);
-  });
+  let rematerializedBoaty = run(() => adam.get('boats')).objectAt(0);
 
   assert.equal(adam.get('boats.length'), 2, 'boats.length correct after rematerialization');
   assert.equal(rematerializedBoaty.get('id'), '1');
diff --git a/tests/integration/relationships/has-many-test.js b/tests/integration/relationships/has-many-test.js
index e4027b5b373..ceccba1b55b 100644
--- a/tests/integration/relationships/has-many-test.js
+++ b/tests/integration/relationships/has-many-test.js
@@ -136,6 +136,88 @@ test("When a hasMany relationship is accessed, the adapter's findMany method sho
   });
 });
 
+test("hasMany + canonical vs currentState + unloadRecord", function(assert) {
+  assert.expect(6);
+
+  let postData = {
+    type: 'user',
+    id: '1',
+    attributes: {
+      name: 'omg'
+    },
+    relationships: {
+      contacts: {
+        data: [
+          {
+            type: 'user',
+            id: 2
+          },
+          {
+            type: 'user',
+            id: 3
+          },
+          {
+            type: 'user',
+            id: 4
+          }
+        ]
+      }
+    }
+  };
+
+  run(() => {
+    env.store.push({
+      data: postData,
+      included: [
+        {
+          type: 'user',
+          id: 2
+        },
+        {
+          type: 'user',
+          id: 3
+        },
+        {
+          type: 'user',
+          id: 4
+        }
+      ]
+    });
+  });
+
+  let user = env.store.peekRecord('user', 1);
+  let contacts = user.get('contacts');
+
+  env.store.adapterFor('user').deleteRecord = function() {
+    return { data: { type: 'user', id: 2 } };
+  };
+
+  assert.deepEqual(contacts.map(c => c.get('id')), ['2','3','4'], 'user should have expected contacts');
+
+  run(() => {
+    contacts.addObject(env.store.createRecord('user', { id: 5 }));
+    contacts.addObject(env.store.createRecord('user', { id: 6 }));
+    contacts.addObject(env.store.createRecord('user', { id: 7 }));
+  });
+
+  assert.deepEqual(contacts.map(c => c.get('id')), ['2','3','4','5','6','7'], 'user should have expected contacts');
+
+  run(() => {
+    env.store.peekRecord('user', 2).unloadRecord();
+    env.store.peekRecord('user', 6).unloadRecord();
+  });
+
+  assert.deepEqual(contacts.map(c => c.get('id')), ['3','4','5','7'], `user's contacts should have expected contacts`);
+  assert.equal(contacts, user.get('contacts'));
+
+  run(() => {
+    contacts.addObject(env.store.createRecord('user', { id: 8 }));
+  });
+
+  assert.deepEqual(contacts.map(c => c.get('id')), ['3','4','5','7','8'], `user's contacts should have expected contacts`);
+  assert.equal(contacts, user.get('contacts'));
+});
+
 test("adapter.findMany only gets unique IDs even if duplicate IDs are present in the hasMany relationship", function(assert) {
   assert.expect(2);
 
@@ -1788,6 +1870,37 @@ testInDebug('A sync hasMany errors out if there are unlaoded records in it', fun
   }, /You looked up the 'comments' relationship on a 'post' with id 1 but some of the associated records were not loaded. Either make sure they are all loaded together with the parent record, or specify that the relationship is async \('DS.hasMany\({ async: true }\)'\)/);
 });
 
+test('After removing and unloading a record, a hasMany relationship should still be valid', function(assert) {
+  const post = run(() => {
+    env.store.push({
+      data: {
+        type: 'post',
+        id: '1',
+        relationships: {
+          comments: {
+            data: [
+              { type: 'comment', id: '1' }
+            ]
+          }
+        }
+      },
+      included: [
+        { type: 'comment', id: '1' }
+      ]
+    });
+    const post = env.store.peekRecord('post', 1);
+    const comments = post.get('comments');
+    const comment = comments.objectAt(0);
+    comments.removeObject(comment);
+    env.store.unloadRecord(comment);
+    assert.equal(comments.get('length'), 0);
+    return post;
+  });
+
+  // Explicitly re-get comments
+  assert.equal(run(post, 'get', 'comments.length'), 0);
+});
+
 test("If reordered hasMany data has been pushed to the store, the many array reflects the ordering change - sync", function(assert) {
   var comment1, comment2, comment3, comment4;
   var post;
diff --git a/tests/integration/store-test.js b/tests/integration/store-test.js
index 4af6741d664..b6a0259cb06 100644
--- a/tests/integration/store-test.js
+++ b/tests/integration/store-test.js
@@ -202,7 +202,7 @@ test("destroying the store correctly cleans everything up", function(assert) {
 
   assert.equal(personWillDestroy.called.length, 1, 'expected person to have recieved willDestroy once');
   assert.equal(carWillDestroy.called.length, 1, 'expected car to recieve willDestroy once');
-  assert.equal(carsWillDestroy.called.length, 1, 'expected cars to recieve willDestroy once');
+  assert.equal(carsWillDestroy.called.length, 1, 'expected person.cars to recieve willDestroy once');
   assert.equal(adapterPopulatedPeopleWillDestroy.called.length, 1, 'expected adapterPopulatedPeople to recieve willDestroy once');
   assert.equal(filterdPeopleWillDestroy.called.length, 1, 'expected filterdPeople.willDestroy to have been called once');
 });
diff --git a/tests/unit/model/relationships/has-many-test.js b/tests/unit/model/relationships/has-many-test.js
index c2559877180..ff6612c3607 100644
--- a/tests/unit/model/relationships/has-many-test.js
+++ b/tests/unit/model/relationships/has-many-test.js
@@ -1394,6 +1394,71 @@ test('deleting an item that is the current state of a belongsTo clears currentSt
   });
 });
 
+test('hasMany.firstObject.unloadRecord should not break that hasMany', function(assert) {
+  const Person = DS.Model.extend({
+    cars: DS.hasMany('car', { async: false }),
+    name: DS.attr()
+  });
+
+  Person.reopenClass({
+    toString() {
+      return 'person';
+    }
+  });
+
+  const Car = DS.Model.extend({
+    name: DS.attr()
+  });
+
+  Car.reopenClass({
+    toString() {
+      return 'car';
+    }
+  });
+
+  let env = setupStore({
+    person: Person,
+    car: Car
+  });
+
+  run(() => {
+    env.store.push({
+      data: [
+        {
+          type: 'person',
+          id: 1,
+          attributes: {
+            name: 'marvin'
+          },
+          relationships: {
+            cars: {
+              data: [
+                { type: 'car', id: 1 },
+                { type: 'car', id: 2 }
+              ]
+            }
+          }
+        },
+        { type: 'car', id: 1, attributes: { name: 'a' } },
+        { type: 'car', id: 2, attributes: { name: 'b' } }
+      ]
+    })
+  });
+
+  let person = env.store.peekRecord('person', 1);
+  let cars = person.get('cars');
+
+  assert.equal(cars.get('length'), 2);
+
+  run(() => {
+    cars.get('firstObject').unloadRecord();
+    assert.equal(cars.get('length'), 1); // unload now..
+    assert.equal(person.get('cars.length'), 1); // unload now..
+  });
+
+  assert.equal(cars.get('length'), 1); // unload now..
+  assert.equal(person.get('cars.length'), 1); // unload now..
+});
 /*
   This test, when passing, affirms that a known limitation of ember-data still exists.
 
@@ -1747,17 +1812,20 @@ test('DS.hasMany proxy is destroyed', function(assert) {
   let { store } = setupStore({ tag: Tag, person: Person });
 
   let tag = run(() => store.createRecord('tag'));
-  let people = tag.get('people');
+  let peopleProxy = tag.get('people');
 
-  return people.then(() => {
+  return peopleProxy.then(people => {
     Ember.run(() => {
       tag.unloadRecord();
-      assert.equal(people.get('isDestroying'), true);
-      assert.equal(people.get('isDestroyed'),  false);
+      assert.equal(people.isDestroying, false, 'people is NOT  destroying sync after unloadRecord');
+      assert.equal(people.isDestroyed, false, 'people is NOT destroyed sync after unloadRecord');
+      assert.equal(peopleProxy.isDestroying, false, 'peopleProxy is destroying sync after unloadRecord');
+      assert.equal(peopleProxy.isDestroyed, false, 'peopleProxy is NOT YET destroyed sync after unloadRecord');
     });
-    assert.equal(people.get('isDestroying'), true);
-    assert.equal(people.get('isDestroyed'), true);
-  })
+
+    assert.equal(peopleProxy.isDestroying, true, 'peopleProxy is destroying after the run post unloadRecord');
+    assert.equal(peopleProxy.isDestroyed, true, 'peopleProxy is destroyed after the run post unloadRecord');
+  });
 });
 
 test('DS.ManyArray is lazy', function(assert) {