Why cached memory doesn’t reclaimed with drop_caches request?

It is very common that your system is suffered from lack of free memory and found that most of the physical memory was consumed by ‘buffer/cache’.

Because ‘buffer/cache’ can be reclaimed when memory is under pressure, user usually doesn’t need to worry about that and most of the time, keeping cache is desirable as it can improve system performance.

But, sometimes, there’s a case that cache doesn’t have meaning such as backup filesystem or handling lots of files that only use once. In that case, early freeing could be a better choice. To drop caches/buffers, you can use following method.

sync
echo 3 > /proc/sys/vm/drop_caches

You can use one of three possible values – 1 : pagecache, 2 : dentries and inodes, 3 : pagecache, dentries and inodes.

But, sometimes, even with above command you can see lots of caches are still in memory. Why it happens?

There are two possible reasons for this.
One is dirty pages that need to be write back to file later.
Second reason is mmaped files. You can see this in following code snippet:

unsigned long __invalidate_mapping_pages(struct address_space *mapping,
                                pgoff_t start, pgoff_t end, bool be_atomic)
{
        struct pagevec pvec;
        pgoff_t next = start;
        unsigned long ret = 0;
        int i;
 
        pagevec_init(&pvec, 0);
        while (next <= end &&
                        pagevec_lookup(&pvec, mapping, next, PAGEVEC_SIZE)) {
                for (i = 0; i index of an
                         * unlocked page.  But we're not allowed to lock these
                         * pages.  So we rely upon nobody altering the ->index
                         * of this (pinned-by-us) page.
                         */
                        index = page->index;
                        if (index > next)
                                next = index;
                        next++;
                        if (lock_failed)
                                continue;
From here >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
                        if (PageDirty(page) || PageWriteback(page))
                                goto unlock;
                        if (page_mapped(page))
                                goto unlock;
To here >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
                        ret += invalidate_complete_page(mapping, page);
unlock:
                        unlock_page(page);
                        if (next > end)
                                break;
                }
                pagevec_release(&pvec);
                if (likely(!be_atomic))
                        cond_resched();
        }
        return ret;
}

As you can see, dirty pages, write back pages, or mapped pages are not reclaimable. To show how mapped pages are handled, I created a simple code.

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
 
int main(int argc, char **argv)
{
        int fd = -1;
        unsigned long size = 4096, status, i, sum;
        char *area;
        pid_t pid;
 
        fd = open("test.img", O_RDONLY);
        size = llseek(fd, 0, SEEK_END);
        llseek(fd, 0, SEEK_SET);
 
        printf("size = %un", size);
 
        area = mmap(NULL, size, PROT_READ | PROT_WRITE,
                    MAP_SHARED | MAP_ANONYMOUS, fd, 0);
 
        printf("area = %pn", area);
        sum = 0;
        for (i = 0; i  /proc/sys/vm/drop_caches");
 
        sleep(5);
        printf("nnAfter ran 'echo 3 > /proc/sys/vm/drop_caches'n");
        system("free");
        munmap(area, size);
 
        sleep(1);
        printf("nnAfter unmap the filen");
        system("free");
 
        exit(0);
}

To test this, you just need to run following steps:

[root@localhost Work]# dd if=/dev/zero of=test.img bs=1M count=1024
1024+0 records in
1024+0 records out
1073741824 bytes (1.1 GB) copied, 2.75285 s, 390 MB/s
[root@localhost Work]# sync; echo 3 > /proc/sys/vm/drop_caches 
[root@localhost Work]# free
             total       used       free     shared    buffers     cached
Mem:       1022992     207796     815196          0        196      20792
-/+ buffers/cache:     186808     836184
Swap:      2031612     240040    1791572
[root@localhost Work]# ./mmap_test 
size = 1073741824
area = 0x7fd6cb45a000
Reading Done


             total       used       free     shared    buffers     cached
Mem:       1022992     431024     591968          0        332     274040
-/+ buffers/cache:     156652     866340
Swap:      2031612     271140    1760472


After ran 'echo 3 > /proc/sys/vm/drop_caches'
             total       used       free     shared    buffers     cached
Mem:       1022992     431016     591976          0        332     273344
-/+ buffers/cache:     157340     865652
Swap:      2031612     271000    1760612


After unmap the file
             total       used       free     shared    buffers     cached
Mem:       1022992     174780     848212          0        408      20760
-/+ buffers/cache:     153612     869380
Swap:      2031612     270960    1760652
[root@localhost Work]#

See, caches are only dropped when mapped pages are freed.

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.