What else is consuming Data Segment reported by avr-size?
Most of the missing data section is probably made of string literals.
When you write something like
Serial.println(&quot;Hello, World!&quot;);

the string &quot;Hello, World!&quot; is implemented by the compiler as an
anonymous array of characters, and the address of this array is
provided to the println() method. Since the array is anonymous, it
doesn't show up in the output of avr-nm. You can check that this is
the case by looking at the actual contents of the section:
avr-objdump -s -j .data x.elf

And you can avoid this by wrapping the strings in the F() macro:
Serial.println(F(&quot;Hello, World!&quot;));

This has the effect of keeping the string in flash, and passing the
flash address to the println() method. Note that this is actually a
different, overloaded method, that knows how to retrieve the character
data from flash.

Edit: Adding some clarifications.
All this complexity about flash vs. RAM storage comes from the fact that
the AVR uses a Harvard architecture, where RAM and flash are
in different address spaces, accessed through different memory buses
using different machine instructions. Unlike C, the C++
language has no notion of address spaces, and the compiler simply
assumes that any pointer to function holds a flash address, whereas any
pointer to data holds a RAM address.
Per the rules of the language, a string literal has the type
const char *, and a function that should be able to accept a string
literal should take a const char * parameter. Since these are pointers
to data, when you dereference them the compiler emits the machine
instructions that are appropriate for reading RAM. This is why the
compiler puts all string literals in the .data section of the flash: at
program startup, the C runtime copies them to the .data section of the
RAM, making them accessible to those machine instructions.
The current solution to avoid this useless copy involves a bunch of
preprocessor macros like F(), PSTR(), PROGMEM,
pgm_read_byte()... that expand to compiler extensions or inline
assembly. These allow you to pass around the flash addresses of string
literals disguised as regular data pointers. These pointers should never
be dereferenced with the regular operators of the language (* and
[]), but only through the pgm_read_*() macros. And they should only
be given to functions that do expect these kind of pointers, like
sprintf_P() or Print::println(const __FlashStringHelper *), which is
the println() that gets invoked when using the F() macro.

Is there any way to put the literal in stack

Yes, there is, but it has to be done explicitly:
PROGMEM const char greeting[] = &quot;Hello, World!&quot;;

void say_hello() {
    char stack_copy[strlen_P(greeting)+1];
    strcpy_P(stack_copy, greeting);
    Serial.println(stack_copy);
}

As you have already noticed, it is preferable to not put the strings in
RAM at all, and instead use functions like sprintf_P that expect
flash-based strings. However, if you need to use a library function that
expects a RAM-based string, you can use this trick to avoid permanently
committing it to RAM.