SQLite User Forum

Why the memory consumption is grow in case of intensive recording? Looks like a memory leaks.
Login

Why the memory consumption is grow in case of intensive recording? Looks like a memory leaks.

(1) By synacker on 2022-02-11 11:05:58 [link] [source]

I did the simple test that use sqlite 3.36 with prepare statements and sqlite3_step.

The test writes records into tests db permanently.

But, the memory consumption is increasing during test - I see it by the top utility.

Are there any ideas, why memory is growing?

Thank you for attention!

#include <iostream>
#include <atomic>
#include <csignal>
#include <sqlite3.h>
#include <string.h>

static constexpr auto sql_create_table =
R"SQL(
CREATE TABLE IF NOT EXISTS TestTable
(
     field1 TEXT,
     field2 TEXT
);
PRAGMA journal_mode = wal;
PRAGMA journal_size_limit = 0;
)SQL";

static constexpr auto sql_insert_record = "INSERT INTO TestTable(field1, field2) VALUES(?, ?)";

std::atomic_bool stop{false};

void SignalHandler( int signum )
{
     std::cout << "Interrupt signal (" << signum << ") received.\n";

     if( signum == SIGTERM || signum == SIGINT )
     {
          stop = true;
     }
}

int main()
{
     std::cout << "sqlite test!\n";

     signal( SIGINT, SignalHandler );
     signal( SIGTERM, SignalHandler );

     // Open

     sqlite3* db;

     auto res = sqlite3_open_v2("test.db",
                                &db,
                                SQLITE_OPEN_FULLMUTEX |
                                SQLITE_OPEN_READWRITE |
                                SQLITE_OPEN_CREATE, nullptr);

     if( res != SQLITE_OK )
     {
          std::cout << "sqlite3_open_v2 function error " << res << std::endl;
          return -1;
     }

     // Create

     res = sqlite3_exec(db, sql_create_table, nullptr, nullptr, nullptr);
     if( res != SQLITE_OK )
     {
          std::cout << "sqlite3_exec function error " << res << std::endl;
          return -1;
     }

     // Write

     sqlite3_stmt* stmt_insert_audit_record;

     res = sqlite3_prepare_v2( db, sql_insert_record, -1, &stmt_insert_audit_record, nullptr );

     if( res != SQLITE_OK )
     {
          std::cout << "sqlite3_prepare_v2 function error " << res << std::endl;
          return -1;
     }

     const char* s1 = "123";
     const char* s2 = "321";

     while( !stop )
     {
          res = sqlite3_bind_text( stmt_insert_audit_record, 1, s1, -1, SQLITE_TRANSIENT );

          if( res != SQLITE_OK )
          {
               std::cout << "sqlite3_bind_text function error " << res << std::endl;
               return -1;
          }

          res = sqlite3_bind_text( stmt_insert_audit_record, 2, s2, -1, SQLITE_TRANSIENT );

          if( res != SQLITE_OK )
          {
               std::cout << "sqlite3_bind_text function error " << res << std::endl;
               return -1;
          }
               
          res = sqlite3_step( stmt_insert_audit_record );

          if( res != SQLITE_DONE )
          {
               std::cout << "sqlite3_step function error " << res << std::endl;
               return -1;
          }

          res = sqlite3_reset( stmt_insert_audit_record );

          if( res != SQLITE_OK )
          {
               std::cout << "sqlite3_reset function error " << res << std::endl;
               return -1;
          }

     } // while

     // Finalize

     if( stmt_insert_audit_record )
     {
          res = sqlite3_finalize( stmt_insert_audit_record );

          if( res != SQLITE_OK )
          {
               std::cout << "sqlite3_finalize function error " << res << std::endl;
               return -1;
          }
     }

     // Close

     if( db )
     {
          res = sqlite3_close_v2( db );
          if( res != SQLITE_OK )
          {
               std::cout << "sqlite3_close_v2 function error " << res << std::endl;
               return -1;
          }
     }

     return 0;
}

(2.1) By Gunter Hick (gunter_hick) on 2022-02-11 11:46:20 edited from 2.0 in reply to 1 [link] [source]

Some memory is required for the internal workings of SQLite.

The "top" utility only indicates the resident set size and total virtual memory used. If the amount of virtual memory increases in a linear fashion depending on the number of transactions, rather than asymptotically reaching a stable amount (when memory requirements can be satisfied by freed memory), that would be indicative of a possible memory leak.

Please use valgrind or a similar tool to determine memory leaks.

SQLite is extensively tested to ensure it does not leak memory. Most probably it is your program that is leaky.

BTW: Rebinding the very same parameters before each step is not required; only parameters that have changed need to be rebound. Also, passing the addresses of strings allocated on the stack is a case for SQLITE_STATIC (because they remain valid until sqlite3_step returns), not for SQLITE_TRANSIENT (which causes a copy to be made and destroyed each time around).

(3) By synacker on 2022-02-11 11:54:42 in reply to 2.1 [link] [source]

Thank you for response!

The memory is grow and not free. Valgrind don't see any leaks. I'm not sure, that this is a memory leak - it looks like a memory leak.

The binding the same value only for test, in real application the data are different, but the memory is growing.

Also, I tried to swap SQLITE_TRANSIENT by SQLITE_STATIC, but the problem still exists.

(4) By Richard Hipp (drh) on 2022-02-11 12:01:08 in reply to 1 [link] [source]

This is probably the memory allocator in the C/C++ standard library failing to reuse previously freed allocations because it is not under memory pressure and doing a fresh allocation is faster than reusing an older one. This is not a bug. And it is not a behavior that SQLite has any control over - it is down inside the C/C++ standard library.

If valgrind and -fsanitize=memory do not report any memory leaks, then there are none.

If you don't like the behavior of the memory allocator in C/C++, SQLite has alternative memory allocators built-in that you can enable using compile-time options.

(5) By Gunter Hick (gunter_hick) on 2022-02-11 12:18:47 in reply to 3 [link] [source]

Note that memory freed by the application does not reduce the size reported by top. It only increases the amount of free heap memory.

If valgrind is unable to find a leak, then there is none and the behaviour has a different cause outside of the scope of what SQLite can do.

You may try using the malloc-free MEMSYS5, which will restrict SQLite to using a block of memory passed to it by your application.

(6) By Simon Slavin (slavin) on 2022-02-11 14:31:33 in reply to 3 [link] [source]

Simplified answer follows:

Your operating system allocates a certain amount of memory to your application. The application will continue to use memory until it reaches that limit. This is because both grabbing memory from the free pool, and releasing it when it's not in use, take time. If your application keeps doing those two things when it doesn't have to it will run more slowly.

When your application reaches its memory allocation, it will stop increasing memory consumption.

Your operating system (which you should name in future discussions like this, since different operating systems handle this differently and one of us may know that one well) will have a way to tell you what the memory allocation is for that process/application. It might be in the top command you mentioned, it might be somewhere else.

(7) By synacker on 2022-02-14 14:17:40 in reply to 3 [link] [source]

Ok, I did additional investigation.

At first, I runed several tests at the same time and memory leaks have increased (the valgrind report is here - https://drive.google.com/file/d/1vEOFOF91mXrgOyvEmNWHr45Isy9-aJYB/view?usp=sharing) .

Secondary, I added sleep after sqlite3_step in 1 ms and memory leaks are gone.

I think that sqlite can't get in time to write and the write buffer therefore increases.

But, why does it occur? I set synchronization to FULL and it means that after sqlite3_step call all data must to be flushed on the storage, but in my case this is not wokring as needed.

(8.1) By Larry Brasfield (larrybr) on 2022-02-14 16:06:30 edited from 8.0 in reply to 7 [link] [source]

I do not see: (1) That your posted code should leak memory; (2) Any evidence that it does leak memory; or (3) any hint as to what "a memory leaks" looks like to you.

I do see: (1) plenty of conclusory assertions without supporting evidence; (2) lack of response to the valid points Gunter has made to help get you (or your thinking) straightened out; and (3) conflicting assertions/intimations from you regarding valgrind results.

You wrote, "and memory leaks have increased (the valgrind report is here ...", implying that said report supports "memory leaks have increased". Earlier, you wrote, "Valgrind don't see any leaks. I'm not sure, that this is a memory leak - it looks like a memory leak." I see that uncertainty as a movement toward the right answer. I looked at the valgrind report, and do not see any leaks reported there. However, since you cut it off before its summary after main() exits, that is not conclusive.

I believe your whole problem is that you are confounding normal patterns of memory use with memory leaks. Because you conflate these two phenomena, I bid you to reconsider Gunter's observation earlier in this thread on this issue rather than blithely ignoring it. (If I were him, I would have given up on helping you after having my perfectly valid input ignored.)

That belief of mine was formed prior to running a little experiment, from just reading your simple program. Nevertheless, I built a program with your codea, ran the program, and observed its memory use behavior. It rapidly grabbed memory and reached a stable memory usage limit which lasted for several minutes, plenty long enough to convince my that my initial reading of your code 3 days ago was correct: That code should not and does not leak memory.


a. It was not exactly your code. I substituted #include <sqlite3.h> with #include "sqlite3.h" for reasons not germane here but which are worth your independent study if you are serious about programming.

(9) By synacker on 2022-02-14 16:33:35 in reply to 8.1 [link] [source]

Thank you for your response!

I use memory leaks definition there in terms of strange behavior looks like a memeory leaks.

I read Gunter points very carefully, therefore, I attached valgrind report for excluding the real memory leaks.

At this time I see that memory is growing, but this is not a memory leak.

If we will accept your test execution result, the once question, that I have at this time - why do you have memory limit during execution, but I don't (or I have a limit, but have not reached it)?

In other terms, what sqlite parameters affect the memory limit during my test? Can I change them?

Thank you for attention!

(10) By Richard Hipp (drh) on 2022-02-14 16:41:16 in reply to 9 [link] [source]

Are you asking about PRAGMA hard_heap_limit=N?

(11) By Gunter Hick (gunter_hick) on 2022-02-15 07:10:46 in reply to 9 [link] [source]

Is there a connection between yourself and the OP in https://sqlite.org/forum/info/b2a9376b22c0e92d ?

It seems a strange coincidence that two independant users of similar apparent command of both the English language and basic behaviour of heap memory allocation should be equally concerned about total heap usage, asking basically identical questions within a small time frame and similarly unwilling and/or unable to act on the advice given.

Just in case it is just a coincidence, I will offer the same advice:

Use MEMSYS5 to confine all SQLite memory allocations to a predetermined block of memory. Pass in either a statically allocated block of memory or a block obtained by malloc*, of a suitable size determined by the maximum amount of memory you are comfortable with SQLite using. That way, any changes in virtual memory use (barring the initial malloc if using a dynamic buffer) are clearly not due to SQLite.

Guidance is found here:

https://sqlite.org/howtocompile.html
https://sqlite.org/compile.html
https://sqlite.org/malloc.html

*Use of stack memory is discouraged, as large buffers allocated on the stack may evade the OS's methods of detecting stack overflow, leading to mysterious SEGV crashes later on.

(12) By Gunter Hick (gunter_hick) on 2022-02-15 07:35:20 in reply to 7 [link] [source]

Which operating system are you running your application on?

If the amount of user space memory used is related to the applications' write rate, this could indicate that data to be written to disk is in fact kept in user-space buffers instead, until the OS has performed the actual writes, and the OS is returning from fflush() calls while data still resides in said buffers.

It is not possible to reliably run SQLite on such a platform.

(13) By synacker on 2022-02-15 14:17:41 in reply to 12 [source]

Thank you for response!

I use linux based OS (debian9 and ubuntu).

Yes, the main theory about current behaviour is user-space buffers overload. Also, I set PRAGMA hard_heap_limit and the sitution is next:

  1. If I set 100kb I got write error

  2. If I set 1mb, I didn't get write error, but memory usage is increased (like memory leaks).

I think, that problem really in user-space buffers, because I can set limit for sqlite heap, but I have no idea, why it occures.

In confirmation of my theory, if I add sleep in 1 ms after sqlite3_step, the memory not increasing.

(14) By synacker on 2022-02-15 15:08:57 in reply to 10 [link] [source]

Thank you for response!

I tried this and explain results in another post

(15) By Larry Brasfield (larrybr) on 2022-02-15 15:46:19 in reply to 9 [link] [source]

I use memory leaks definition there in terms of strange behavior looks like a memeory leaks.

You have been misusing the term "memory leak". (I understood that you used it for what you thought might be called a "memory leak".) Your misuse of the term has added confusion and misdirection into the discussion.

You should understand, if you intend to have effective, efficient, to-the-point discussions with software developers, that "memory leak" denotes a very specific concept, one much narrower than "uses memory in strange ways I do not yet understand."

Wikipedia has published an article explaining the "memory leak" concept which I recommend you read instead of throwing the term into discussions about something else entirely.

(16) By synacker on 2022-02-15 16:06:54 in reply to 15 [link] [source]

I don't aggree.

For bug reports the strong definitions are required.

But, I started a discussion about strange behavior, that looks like memory leaks. And point about sanitizers and valgrind provides reasons for excluding real memory leaks in stadnard terms.

Thank you for attention, your points and points from other collegues will provides to get explaination about the problem with memory consuming strange behavior, I'm sure.

(17) By synacker on 2022-02-16 11:06:49 in reply to 12 [link] [source]

I added sync after sqlite3_reset and memory is not growing.

Does it mean, that sqlite with full syncronization mode not wait until data will saved on storage?

(18) By Simon Slavin (slavin) on 2022-02-16 11:18:51 in reply to 17 [link] [source]

sync is an instruction to your operating system. SQLite cannot even tell that you have done it. I think something is strange about the way you are looking at memory usage.

What value do you have for this PRAGMA ?

https://sqlite.org/pragma.html#pragma_synchronous

You can find out by executing

PRAGMA schema.synchronous

and treating the result as if it is the result of a SELECT command.

(19) By synacker on 2022-02-16 13:02:33 in reply to 18 [link] [source]

I changed SQL for table creation:

static constexpr auto sql_create_table =
R"SQL(
CREATE TABLE IF NOT EXISTS TestTable
(
     field1 TEXT,
     field2 TEXT
);
PRAGMA journal_mode = wal;
PRAGMA journal_size_limit = 0;
PRAGMA synchronous = FULL;
)SQL";

The result is the same - without sync, memory is growing.

I see memory usage at the top utility and with valgrind utility - memory is growing, but this is not a memory leaks in standard terms.