diff --git a/rest_framework/response.py b/rest_framework/response.py
index bf0663255..c7d2ce5b6 100644
--- a/rest_framework/response.py
+++ b/rest_framework/response.py
@@ -13,6 +13,29 @@ from django.utils.six.moves.http_client import responses
from rest_framework.serializers import Serializer
+def cleanup(response):
+ """ Cleanup circular reference between view/request/response object
+
+ This reduce load on GC and help to keep low memory usage even
+ if some response are larger.
+ """
+ view = response.renderer_context.get('view')
+ request = response.renderer_context.get('request')
+
+ if view:
+ view.response = None
+ view.request = None
+
+ if request:
+ request.parser_context.clear()
+
+ response.renderer_context.clear()
+
+ # Re-add request in renderer_context. It does not seem to cause
+ # circular reference and is needed for tests.
+ response.renderer_context['request'] = request
+
+
class Response(SimpleTemplateResponse):
"""
An HttpResponse that allows its data to be rendered into
@@ -48,6 +71,8 @@ class Response(SimpleTemplateResponse):
for name, value in six.iteritems(headers):
self[name] = value
+ self.add_post_render_callback(cleanup)
+
@property
def rendered_content(self):
renderer = getattr(self, 'accepted_renderer', None)
diff --git a/rest_framework/viewsets.py b/rest_framework/viewsets.py
index 9a85049bc..a7f1b9aad 100644
--- a/rest_framework/viewsets.py
+++ b/rest_framework/viewsets.py
@@ -100,7 +100,16 @@ class ViewSetMixin(object):
self.kwargs = kwargs
# And continue as usual
- return self.dispatch(request, *args, **kwargs)
+ result = self.dispatch(request, *args, **kwargs)
+
+ # break our circular reference before finishing
+ for method in actions:
+ delattr(self, method)
+
+ if hasattr(self, 'head'):
+ delattr(self, 'head')
+
+ return result
# take name and docstring from class
update_wrapper(view, cls, updated=())
diff --git a/tests/test_generics.py b/tests/test_generics.py
index c0ff1c5c4..b60ff77e3 100644
--- a/tests/test_generics.py
+++ b/tests/test_generics.py
@@ -167,7 +167,7 @@ class TestRootView(TestCase):
request = factory.post('/', data, HTTP_ACCEPT='text/html')
response = self.view(request).render()
expected_error = 'Ensure this field has no more than 100 characters.'
- assert expected_error in response.rendered_content.decode('utf-8')
+ assert expected_error in response.content.decode('utf-8')
EXPECTED_QUERIES_FOR_PUT = 2
@@ -314,7 +314,7 @@ class TestInstanceView(TestCase):
request = factory.put('/', data, HTTP_ACCEPT='text/html')
response = self.view(request, pk=1).render()
expected_error = 'Ensure this field has no more than 100 characters.'
- assert expected_error in response.rendered_content.decode('utf-8')
+ assert expected_error in response.content.decode('utf-8')
class TestFKInstanceView(TestCase):