In addition to the other answers, I'd like to add that when carving up RAM between stack and heap space, that you also need to consider the space for static non-constant data (e.g. file globals, function statics, and program-wide globals from a C perspective, and probably others for C++).
How stack/heap allocation works
It is worth noting that the startup assembly file is one way of defining the region; the toolchain (both your build environment and run-time environment) mostly care about the symbols which define the start of stackspace (used to store the initial stack pointer in the Vector Table) and the start and end of heap space (used by the dynamic memory allocator, typically provided by your libc)
In OP's example, only 2 symbols are defined, a size of stack at 1kiB and a size of heap at 0B. These values get used elsewhere to actually produce the stack and heap spaces
In @Gilles example, the sizes are defined and used in the assembly file to set a stack space starting wherever and lasting the size, identified by the symbol Stack_Mem and sets a label __initial_sp at the end. Likewise for the heap, where the space is the symbol Heap_Mem (0.5kiB in size), but with labels at the beginning and end (__heap_base and __heap_limit).
These get processed by the linker, which won't allocate anything within the stack space and heap space because that memory is occupied (by Stack_Mem and Heap_Mem symbols), but it can place those memories and all of the globals wherever it needs. The labels end up being symbols with no length at the given addresses. The __initial_sp is used directly for the vector table at link time, and the __heap_base and __heap_limit by your runtime code. The actual addresses of the symbols are assigned by the linker based on where it placed them.
As I aluded to above, these symbols don't actually have to come from a startup.s file. They can come from your linker configuration (Scatter Load file in Keil, linkerscript in GNU), and in those you can have finer grained control over placement. For example, you can force the stack to be at the beginning or end of RAM, or keep your globals away from the heap, or whatever you want. You can even specify that the HEAP or STACK just occupy whatever RAM is left over after globals are placed. NOTE though that you have to be careful that adding more static variables that your other memory will decrease.
However, each toolchain is different, and how to write the configuration file and what symbols your dynamic memory allocator will use will have to come from the documentation of your particular environment.
As to how to determine stack size, many toolchains can give you a maximum stack depth by analyzing the function call trees of your program, IF you don't use recursion or function pointers. If you do use those, estimating a stack size and pre-filling it with cardinal values (perhaps via the entry function before main) and then checking after your program has run for a while where the max depth was (which is where the cardinal values end). If you have fully exercised your program to its limits, you'll know fairly accurately whether you can shrink the stack or, if your program crashes or no cardinal values are left, that you need to increase the stack and try again.
Determining heap size is a bit more application dependant. If you only do dynamic allocation during startup, you can just add up the space required in your startup code (plus some overhead for memory management). If you have access to the source of your memory manager, you can know exactly what the overhead is, and possibly even write code to walk the memory to give you usage information. For applications that need dynamic runtime memory (e.g. allocating buffers for inbound ethernet frames) the best I can suggest is to carefully hone your stacksize and give the Heap everything that is left over after stack and statics.
Final note (RTOS)
OP's question was tagged for bare-metal, but I want to add a note for RTOSes. Often (always?) each task/process/thread (I'll just write task here on out for simplicity) will be assigned a stack size when the task is created, an in addition to task stacks, there will likely be a small OS stack (used for interrupts and such)
The task accounting structures and the stacks have to be allocated from somewhere, and this will often be from the overall heap space of your application. In these instances, your initial stack size often won't matter, because the OS will only use it during initialization. I have seen, for example, specifying ALL remaining space during linking be allocated to the HEAP and placing the initial stack pointer at the end of the heap to grow into the heap, knowing that the OS will allocate starting from the beginning of the heap and will allocate the OS stack just before abandoning the initial_sp stack. Then all of the space is used for allocating task stacks and other dynamically allocated memory.