Use of progmem in ESP8266 vs AVR and also how to handle large dynamic strings
On the ESP8266 flash is a very special case. Because the ESP8266 itself does not have any flash, but instead uses an external SPI-connected flash chip, accessing it is not an easy or technically obvious operation.

Reading from the flash chip is slow. Very very slow (by comparison to reading RAM).  Because of this where possible certain things are copied into RAM. Also, there are different sections of RAM that are dedicated to different purposes.


dram0 - This is your "Data" RAM. It's where your variables and such are stored (80kB total).
iram1 - Instruction RAM. It's here that interrupt routines need to be stored for rapid execution (32kB).
icache - Instruction cache. 16kB of data that is cached when reading from the flash chip. Executed code usually gets copied here.


Normally when you execute code the block of code that the instructions you are executing are copied from flash into the icache memory block and then executed. This is reasonably fast (reasonably in that there may be brief delays while copying the data from flash, but fast when actually executing. I don't know how efficient the cache is, whether it's split into different blocks that are loaded in the background by the memory controller as needed, but it seems to be a pretty efficient system that is used in practice).

Because of this disassociation of the CPU and flash string constants, unless specified otherwise, will be copied into dram0 during startup. This makes it very fast and simple to get at the data. Anything that you don't specify as PROGMEM will get copied into dram0. Note that you don't get all 80kB of the RAM to play with since the SDK uses a portion of it. You only get about 50kB at most (station mode - softap mode will use more). 

Now... what does PROGMEM do on the ESP8266 then?  Well, it adds an attribute to the definition that tells the linker to place the data in a specific section in flash (.irom.text.&lt;filename&gt;.&lt;line&gt;.&lt;number&gt;). This prevents it being copied into dram0 at startup.

So how do you get at it? That's where the *_P variants of the string functions (and certain overloaded functions in the Arduino core) come into play.

The whole of the flash chip is mapped (or can be, depending on register settings) into the ESP8266's memory map (address 0x40200000). Which means that it's possible to get directly at the data. However since this access doesn't go through the icache RAM block it's slow. Very very slow. About 12x slower by some accounts. Also because data can be in different locations within the chip the addresses of your string data can't be directly hard coded. There has to be some form of "it's over there" redirection when reading the data (for example if you use OTA updates you get two distinct blocks of flash where your code can be stored - the currently executing code and the previously executing version). The *_P variants of the string functions take that into account and look up where in flash the current code is located before reading from it. 

So to summarise: Not using PROGMEM gives you fast access to your string data at the cost of RAM, and limits the size of the data you can use. Using PROGMEM removes the size restriction, doesn't use RAM, but is considerably slower.

To specifically answer your questions:


The string data gets copied to RAM at startup.
The string data gets copied to RAM at startup.
The string data gets loaded from flash (using *_P variants of functions) when the String object is constructed (RAM is allocated on the heap at that moment in time). The RAM is freed when the String object is destroyed (goes out of scope).
The compiler has recently had an update that will deduplicate constant PROGMEM strings.
As mentioned above *_P copies directly from flash and is slow, but has no size limitation.