Q:

Why can't the JVM ensure garbage collection

This is not a duplicate as the thread mentioned as duplicate only tells you why to not use System.gc(), while I do know that, that's not my question.
Using System.gc() or Runtime.getRuntime().gc() won't always execute garbage collection, it just requests it and it can even be ignored by the JVM. 
Is there a reason behind this? Is it "random"? Since I don't think random even exists in programming, I'm curious why it sometimes doesn't collect it, and sometimes does and also at different times.

A:

Yes.  There is a very good reason.

First you need to understand a couple of facts about garbage collection in general:

Running the garbage collector is expensive.
Running the garbage collector (repeatedly) at the wrong time can be catastrophically inefficient1.
Next to impossible for a typical application to predict when the GC can be run most efficiently.

So to your questions:

Using System.gc() or Runtime.getRuntime().gc() won't always execute garbage collection, it just requests it and it can even be ignored by the JVM.  Is there a reason behind this? 

Yes.  It is primarily to guard against the catastrophic behavior effects that naive programmers can cause by calling gc() at the wrong time.

Is it "random"?

Nope.  It is nothing to do with randomness.
In practice, typical JVMs have a command line switch that determines whether gc() calls are ignored, or not.  This allows the user / deployer / integrator / whoever to mitigate the poor choice made by the programmer.
But note that it cannot be overridden from Java code.  That would defeat the purpose of making this a command line switch.

I'm curious why it sometimes doesn't collect it, and sometimes does and also at different times.

The normal behavior of a JVM is to try to run the garbage collector when it is most efficient to do so.  The JVM can optimize two ways:

It can optimize to maximize the throughput of the collectors; i.e. to minimize the CPU time spent on collection
It can optimize to minimize the length of GC pauses; i.e. the times where the JVM has to freeze all application threads during the collection.

It is complicated.  From the perspective of an external observer (who / which doesn't have access to the heap stats, etcetera) it may be hard to understand why the GC runs at a given point in time.  But is certainly not random.

1 - One of the counter-intuitive properties of a GC algorithm is that the cost of collecting is (in most cases) dominated by the costs of tracing non-garbage objects and moving in memory to coalesce the freed space.  So if you call the GC when there is not much garbage to collect, you will end up tracing / moving large amounts of non-garbage for little actual benefit.  By contrast, the JVM has a better insight on when it is a good time to collect.  For a start, it knows how much space is left in each pool at any point in time.
As well as being counter-intuitive, this behavior gets worse as the heap gets bigger.  So a programmer can write code that uses gc() in the wrong way, and not notice that there is a performance problem.  The performance only becomes an issue when the application is run with a production workload / problem size.  Combine this with the effects of using the heap to cache things, and / or memory leaks.....

